Merge branch 'main' into features/i8n
# Conflicts: # frontend/app.js
This commit is contained in:
commit
83e9ebbd3d
|
|
@ -685,10 +685,10 @@ async def move_note_endpoint(request: Request, data: dict):
|
||||||
if not old_path or not new_path:
|
if not old_path or not new_path:
|
||||||
raise HTTPException(status_code=400, detail="Both oldPath and newPath required")
|
raise HTTPException(status_code=400, detail="Both oldPath and newPath required")
|
||||||
|
|
||||||
success = move_note(config['storage']['notes_dir'], old_path, new_path)
|
success, error_msg = move_note(config['storage']['notes_dir'], old_path, new_path)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
raise HTTPException(status_code=500, detail="Failed to move note")
|
raise HTTPException(status_code=400, detail=error_msg or "Failed to move note")
|
||||||
|
|
||||||
# Run plugin hooks
|
# Run plugin hooks
|
||||||
plugin_manager.run_hook('on_note_save', note_path=new_path, content='')
|
plugin_manager.run_hook('on_note_save', note_path=new_path, content='')
|
||||||
|
|
@ -716,10 +716,10 @@ async def move_folder_endpoint(request: Request, data: dict):
|
||||||
if not old_path or not new_path:
|
if not old_path or not new_path:
|
||||||
raise HTTPException(status_code=400, detail="Both oldPath and newPath required")
|
raise HTTPException(status_code=400, detail="Both oldPath and newPath required")
|
||||||
|
|
||||||
success = move_folder(config['storage']['notes_dir'], old_path, new_path)
|
success, error_msg = move_folder(config['storage']['notes_dir'], old_path, new_path)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
raise HTTPException(status_code=500, detail="Failed to move folder")
|
raise HTTPException(status_code=400, detail=error_msg or "Failed to move folder")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
|
|
@ -744,10 +744,10 @@ async def rename_folder_endpoint(request: Request, data: dict):
|
||||||
if not old_path or not new_path:
|
if not old_path or not new_path:
|
||||||
raise HTTPException(status_code=400, detail="Both oldPath and newPath required")
|
raise HTTPException(status_code=400, detail="Both oldPath and newPath required")
|
||||||
|
|
||||||
success = rename_folder(config['storage']['notes_dir'], old_path, new_path)
|
success, error_msg = rename_folder(config['storage']['notes_dir'], old_path, new_path)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
raise HTTPException(status_code=500, detail="Failed to rename folder")
|
raise HTTPException(status_code=400, detail=error_msg or "Failed to rename folder")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
|
|
|
||||||
|
|
@ -73,57 +73,70 @@ def get_all_folders(notes_dir: str) -> List[str]:
|
||||||
return sorted(folders)
|
return sorted(folders)
|
||||||
|
|
||||||
|
|
||||||
def move_note(notes_dir: str, old_path: str, new_path: str) -> bool:
|
def move_note(notes_dir: str, old_path: str, new_path: str) -> tuple[bool, str]:
|
||||||
"""Move a note to a different location"""
|
"""Move a note to a different location
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (success: bool, error_message: str)
|
||||||
|
"""
|
||||||
old_full_path = Path(notes_dir) / old_path
|
old_full_path = Path(notes_dir) / old_path
|
||||||
new_full_path = Path(notes_dir) / new_path
|
new_full_path = Path(notes_dir) / new_path
|
||||||
|
|
||||||
# Security checks
|
# Security checks
|
||||||
if not validate_path_security(notes_dir, old_full_path) or \
|
if not validate_path_security(notes_dir, old_full_path):
|
||||||
not validate_path_security(notes_dir, new_full_path):
|
return False, "Invalid source path"
|
||||||
return False
|
if not validate_path_security(notes_dir, new_full_path):
|
||||||
|
return False, "Invalid destination path"
|
||||||
|
|
||||||
if not old_full_path.exists():
|
if not old_full_path.exists():
|
||||||
return False
|
return False, f"Source note does not exist: {old_path}"
|
||||||
|
|
||||||
# Check if target already exists (prevent overwriting)
|
# Check if target already exists (prevent overwriting)
|
||||||
if new_full_path.exists():
|
if new_full_path.exists():
|
||||||
return False
|
return False, f"A note already exists at: {new_path}"
|
||||||
|
|
||||||
# Invalidate cache for old path
|
# Invalidate cache for old path
|
||||||
old_key = str(old_full_path)
|
old_key = str(old_full_path)
|
||||||
if old_key in _tag_cache:
|
if old_key in _tag_cache:
|
||||||
del _tag_cache[old_key]
|
del _tag_cache[old_key]
|
||||||
|
|
||||||
|
try:
|
||||||
# Create parent directory if needed
|
# Create parent directory if needed
|
||||||
new_full_path.parent.mkdir(parents=True, exist_ok=True)
|
new_full_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Move the file
|
# Move the file
|
||||||
old_full_path.rename(new_full_path)
|
old_full_path.rename(new_full_path)
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"Failed to move file: {str(e)}"
|
||||||
|
|
||||||
# Note: We don't automatically delete empty folders to preserve user's folder structure
|
# Note: We don't automatically delete empty folders to preserve user's folder structure
|
||||||
|
|
||||||
return True
|
return True, ""
|
||||||
|
|
||||||
|
|
||||||
def move_folder(notes_dir: str, old_path: str, new_path: str) -> bool:
|
def move_folder(notes_dir: str, old_path: str, new_path: str) -> tuple[bool, str]:
|
||||||
"""Move a folder to a different location"""
|
"""Move a folder to a different location
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (success: bool, error_message: str)
|
||||||
|
"""
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
old_full_path = Path(notes_dir) / old_path
|
old_full_path = Path(notes_dir) / old_path
|
||||||
new_full_path = Path(notes_dir) / new_path
|
new_full_path = Path(notes_dir) / new_path
|
||||||
|
|
||||||
# Security checks
|
# Security checks
|
||||||
if not validate_path_security(notes_dir, old_full_path) or \
|
if not validate_path_security(notes_dir, old_full_path):
|
||||||
not validate_path_security(notes_dir, new_full_path):
|
return False, "Invalid source path"
|
||||||
return False
|
if not validate_path_security(notes_dir, new_full_path):
|
||||||
|
return False, "Invalid destination path"
|
||||||
|
|
||||||
if not old_full_path.exists() or not old_full_path.is_dir():
|
if not old_full_path.exists() or not old_full_path.is_dir():
|
||||||
return False
|
return False, f"Source folder does not exist: {old_path}"
|
||||||
|
|
||||||
# Check if target already exists
|
# Check if target already exists
|
||||||
if new_full_path.exists():
|
if new_full_path.exists():
|
||||||
return False
|
return False, f"A folder already exists at: {new_path}"
|
||||||
|
|
||||||
# Invalidate cache for all notes in this folder
|
# Invalidate cache for all notes in this folder
|
||||||
global _tag_cache
|
global _tag_cache
|
||||||
|
|
@ -132,18 +145,21 @@ def move_folder(notes_dir: str, old_path: str, new_path: str) -> bool:
|
||||||
for key in keys_to_delete:
|
for key in keys_to_delete:
|
||||||
del _tag_cache[key]
|
del _tag_cache[key]
|
||||||
|
|
||||||
|
try:
|
||||||
# Create parent directory if needed
|
# Create parent directory if needed
|
||||||
new_full_path.parent.mkdir(parents=True, exist_ok=True)
|
new_full_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Move the folder
|
# Move the folder
|
||||||
shutil.move(str(old_full_path), str(new_full_path))
|
shutil.move(str(old_full_path), str(new_full_path))
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"Failed to move folder: {str(e)}"
|
||||||
|
|
||||||
# Note: We don't automatically delete empty folders to preserve user's folder structure
|
# Note: We don't automatically delete empty folders to preserve user's folder structure
|
||||||
|
|
||||||
return True
|
return True, ""
|
||||||
|
|
||||||
|
|
||||||
def rename_folder(notes_dir: str, old_path: str, new_path: str) -> bool:
|
def rename_folder(notes_dir: str, old_path: str, new_path: str) -> tuple[bool, str]:
|
||||||
"""Rename a folder (same as move but for clarity)"""
|
"""Rename a folder (same as move but for clarity)"""
|
||||||
return move_folder(notes_dir, old_path, new_path)
|
return move_folder(notes_dir, old_path, new_path)
|
||||||
|
|
||||||
|
|
|
||||||
143
frontend/app.js
143
frontend/app.js
|
|
@ -1170,14 +1170,13 @@ function noteApp() {
|
||||||
|
|
||||||
if (this.favoritesSet.has(path)) {
|
if (this.favoritesSet.has(path)) {
|
||||||
// Remove from favorites
|
// Remove from favorites
|
||||||
this.favoritesSet.delete(path);
|
|
||||||
this.favorites = this.favorites.filter(f => f !== path);
|
this.favorites = this.favorites.filter(f => f !== path);
|
||||||
} else {
|
} else {
|
||||||
// Add to favorites
|
// Add to favorites
|
||||||
this.favoritesSet.add(path);
|
this.favorites = [...this.favorites, path];
|
||||||
this.favorites.push(path);
|
|
||||||
}
|
}
|
||||||
|
// Recreate Set from array for consistency
|
||||||
|
this.favoritesSet = new Set(this.favorites);
|
||||||
this.saveFavorites();
|
this.saveFavorites();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1185,10 +1184,14 @@ function noteApp() {
|
||||||
get favoriteNotes() {
|
get favoriteNotes() {
|
||||||
return this.favorites
|
return this.favorites
|
||||||
.map(path => {
|
.map(path => {
|
||||||
const note = this.notes.find(n => n.path === path);
|
// Find note by exact path or case-insensitive match
|
||||||
|
let note = this.notes.find(n => n.path === path);
|
||||||
|
if (!note) {
|
||||||
|
note = this.notes.find(n => n.path.toLowerCase() === path.toLowerCase());
|
||||||
|
}
|
||||||
if (!note) return null;
|
if (!note) return null;
|
||||||
return {
|
return {
|
||||||
path: note.path,
|
path: note.path, // Use actual path from notes (fixes case issues)
|
||||||
name: note.path.split('/').pop().replace('.md', ''),
|
name: note.path.split('/').pop().replace('.md', ''),
|
||||||
folder: note.folder || ''
|
folder: note.folder || ''
|
||||||
};
|
};
|
||||||
|
|
@ -2040,15 +2043,18 @@ function noteApp() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: Capture dragged item info immediately before async operations
|
||||||
|
// because ondragend may fire and clear these values
|
||||||
|
const draggedNotePath = this.draggedNote || (this.draggedItem?.type === 'note' ? this.draggedItem.path : null);
|
||||||
|
const draggedFolderPath = this.draggedFolder;
|
||||||
|
|
||||||
// Handle note drop into folder
|
// Handle note drop into folder
|
||||||
if (this.draggedNote) {
|
if (draggedNotePath) {
|
||||||
const note = this.notes.find(n => n.path === this.draggedNote);
|
const note = this.notes.find(n => n.path === draggedNotePath);
|
||||||
if (!note) return;
|
if (!note) return;
|
||||||
|
|
||||||
// Don't allow moving images to folders
|
// Don't allow moving images to folders
|
||||||
if (note.type === 'image') {
|
if (note.type === 'image') {
|
||||||
this.draggedNote = null;
|
|
||||||
this.draggedItem = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2056,82 +2062,112 @@ function noteApp() {
|
||||||
const filename = note.path.split('/').pop();
|
const filename = note.path.split('/').pop();
|
||||||
const newPath = targetFolderPath ? `${targetFolderPath}/${filename}` : filename;
|
const newPath = targetFolderPath ? `${targetFolderPath}/${filename}` : filename;
|
||||||
|
|
||||||
if (newPath === this.draggedNote) {
|
if (newPath === draggedNotePath) {
|
||||||
this.draggedNote = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this note is favorited BEFORE the async call
|
||||||
|
const wasFavorited = this.favoritesSet.has(draggedNotePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/notes/move', {
|
const response = await fetch('/api/notes/move', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
oldPath: this.draggedNote,
|
oldPath: draggedNotePath,
|
||||||
newPath: newPath
|
newPath: newPath
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
await this.loadNotes();
|
// Update favorites if the moved note was favorited
|
||||||
|
if (wasFavorited) {
|
||||||
|
// Update Array (create new array for reactivity)
|
||||||
|
const newFavorites = this.favorites.map(f => f === draggedNotePath ? newPath : f);
|
||||||
|
this.favorites = newFavorites;
|
||||||
|
// Recreate Set from array for consistency
|
||||||
|
this.favoritesSet = new Set(newFavorites);
|
||||||
|
this.saveFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
// Keep current note open if it was the moved note
|
// Keep current note open if it was the moved note
|
||||||
if (this.currentNote === this.draggedNote) {
|
const wasCurrentNote = this.currentNote === draggedNotePath;
|
||||||
|
|
||||||
|
await this.loadNotes();
|
||||||
|
|
||||||
|
if (wasCurrentNote) {
|
||||||
this.currentNote = newPath;
|
this.currentNote = newPath;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(this.t('move.failed_note'));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
alert(errorData.detail || this.t('move.failed_note'));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to move note:', error);
|
console.error('Failed to move note:', error);
|
||||||
alert(this.t('move.failed_note'));
|
alert(this.t('move.failed_note'));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.draggedNote = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle folder drop into folder
|
// Handle folder drop into folder
|
||||||
if (this.draggedFolder) {
|
if (draggedFolderPath) {
|
||||||
// Prevent dropping folder into itself or its subfolders
|
// Prevent dropping folder into itself or its subfolders
|
||||||
if (targetFolderPath === this.draggedFolder ||
|
if (targetFolderPath === draggedFolderPath ||
|
||||||
targetFolderPath.startsWith(this.draggedFolder + '/')) {
|
targetFolderPath.startsWith(draggedFolderPath + '/')) {
|
||||||
alert(this.t('folders.cannot_move_into_self'));
|
alert(this.t('folders.cannot_move_into_self'));
|
||||||
this.draggedFolder = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const folderName = this.draggedFolder.split('/').pop();
|
const folderName = draggedFolderPath.split('/').pop();
|
||||||
const newPath = targetFolderPath ? `${targetFolderPath}/${folderName}` : folderName;
|
const newPath = targetFolderPath ? `${targetFolderPath}/${folderName}` : folderName;
|
||||||
|
|
||||||
if (newPath === this.draggedFolder) {
|
if (newPath === draggedFolderPath) {
|
||||||
this.draggedFolder = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture favorites info before async call
|
||||||
|
const oldPrefix = draggedFolderPath + '/';
|
||||||
|
const newPrefix = newPath + '/';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/folders/move', {
|
const response = await fetch('/api/folders/move', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
oldPath: this.draggedFolder,
|
oldPath: draggedFolderPath,
|
||||||
newPath: newPath
|
newPath: newPath
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
// Update favorites that were in the moved folder
|
||||||
|
const newFavorites = this.favorites.map(f => {
|
||||||
|
if (f.startsWith(oldPrefix)) {
|
||||||
|
return f.replace(oldPrefix, newPrefix);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
});
|
||||||
|
// Check if anything changed
|
||||||
|
if (JSON.stringify(newFavorites) !== JSON.stringify(this.favorites)) {
|
||||||
|
this.favorites = newFavorites;
|
||||||
|
this.favoritesSet = new Set(newFavorites);
|
||||||
|
this.saveFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
await this.loadNotes();
|
await this.loadNotes();
|
||||||
// Update current note path if it was in the moved folder
|
// Update current note path if it was in the moved folder
|
||||||
if (this.currentNote && this.currentNote.startsWith(this.draggedFolder + '/')) {
|
if (this.currentNote && this.currentNote.startsWith(oldPrefix)) {
|
||||||
this.currentNote = this.currentNote.replace(this.draggedFolder, newPath);
|
this.currentNote = this.currentNote.replace(oldPrefix, newPrefix);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(this.t('move.failed_folder'));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
alert(errorData.detail || this.t('move.failed_folder'));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to move folder:', error);
|
console.error('Failed to move folder:', error);
|
||||||
alert(this.t('move.failed_folder'));
|
alert(this.t('move.failed_folder'));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.draggedFolder = null;
|
|
||||||
this.dropTarget = null;
|
this.dropTarget = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -2666,9 +2702,25 @@ function noteApp() {
|
||||||
this.expandedFolders.add(newPath);
|
this.expandedFolders.add(newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update favorites that were in the renamed folder
|
||||||
|
const folderPrefix = folderPath + '/';
|
||||||
|
const newFolderPrefix = newPath + '/';
|
||||||
|
const newFavorites = this.favorites.map(f => {
|
||||||
|
if (f.startsWith(folderPrefix)) {
|
||||||
|
return f.replace(folderPrefix, newFolderPrefix);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
});
|
||||||
|
// Check if anything changed
|
||||||
|
if (JSON.stringify(newFavorites) !== JSON.stringify(this.favorites)) {
|
||||||
|
this.favorites = newFavorites;
|
||||||
|
this.favoritesSet = new Set(newFavorites);
|
||||||
|
this.saveFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
// Update current note path if it's in the renamed folder
|
// Update current note path if it's in the renamed folder
|
||||||
if (this.currentNote && this.currentNote.startsWith(folderPath + '/')) {
|
if (this.currentNote && this.currentNote.startsWith(folderPrefix)) {
|
||||||
this.currentNote = this.currentNote.replace(folderPath, newPath);
|
this.currentNote = this.currentNote.replace(folderPrefix, newFolderPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.loadNotes();
|
await this.loadNotes();
|
||||||
|
|
@ -2696,8 +2748,17 @@ function noteApp() {
|
||||||
// Remove from expanded folders
|
// Remove from expanded folders
|
||||||
this.expandedFolders.delete(folderPath);
|
this.expandedFolders.delete(folderPath);
|
||||||
|
|
||||||
|
// Remove any favorites that were in the deleted folder
|
||||||
|
const folderPrefix = folderPath + '/';
|
||||||
|
const newFavorites = this.favorites.filter(f => !f.startsWith(folderPrefix));
|
||||||
|
if (newFavorites.length !== this.favorites.length) {
|
||||||
|
this.favorites = newFavorites;
|
||||||
|
this.favoritesSet = new Set(newFavorites);
|
||||||
|
this.saveFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
// Clear current note if it was in the deleted folder
|
// Clear current note if it was in the deleted folder
|
||||||
if (this.currentNote && this.currentNote.startsWith(folderPath + '/')) {
|
if (this.currentNote && this.currentNote.startsWith(folderPrefix)) {
|
||||||
this.currentNote = '';
|
this.currentNote = '';
|
||||||
this.noteContent = '';
|
this.noteContent = '';
|
||||||
}
|
}
|
||||||
|
|
@ -3011,6 +3072,14 @@ function noteApp() {
|
||||||
// Delete old note
|
// Delete old note
|
||||||
await fetch(`/api/notes/${oldPath}`, { method: 'DELETE' });
|
await fetch(`/api/notes/${oldPath}`, { method: 'DELETE' });
|
||||||
|
|
||||||
|
// Update favorites if the renamed note was favorited
|
||||||
|
if (this.favoritesSet.has(oldPath)) {
|
||||||
|
const newFavorites = this.favorites.map(f => f === oldPath ? newPath : f);
|
||||||
|
this.favorites = newFavorites;
|
||||||
|
this.favoritesSet = new Set(newFavorites);
|
||||||
|
this.saveFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
this.currentNote = newPath;
|
this.currentNote = newPath;
|
||||||
await this.loadNotes();
|
await this.loadNotes();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3039,6 +3108,14 @@ function noteApp() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
// Remove from favorites if it was favorited
|
||||||
|
if (this.favoritesSet.has(notePath)) {
|
||||||
|
const newFavorites = this.favorites.filter(f => f !== notePath);
|
||||||
|
this.favorites = newFavorites;
|
||||||
|
this.favoritesSet = new Set(newFavorites);
|
||||||
|
this.saveFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
// If the deleted note is currently open, clear it
|
// If the deleted note is currently open, clear it
|
||||||
if (this.currentNote === notePath) {
|
if (this.currentNote === notePath) {
|
||||||
this.currentNote = '';
|
this.currentNote = '';
|
||||||
|
|
|
||||||
|
|
@ -1262,6 +1262,7 @@
|
||||||
class="flex items-center justify-between px-2 py-1.5 rounded cursor-pointer group"
|
class="flex items-center justify-between px-2 py-1.5 rounded cursor-pointer group"
|
||||||
:class="{'bg-opacity-50': currentNote === fav.path}"
|
:class="{'bg-opacity-50': currentNote === fav.path}"
|
||||||
:style="currentNote === fav.path ? 'background-color: var(--accent-primary); color: white;' : 'color: var(--text-secondary);'"
|
:style="currentNote === fav.path ? 'background-color: var(--accent-primary); color: white;' : 'color: var(--text-secondary);'"
|
||||||
|
:title="fav.path"
|
||||||
@mouseenter="if (currentNote !== fav.path) $el.style.backgroundColor = 'var(--bg-hover)'"
|
@mouseenter="if (currentNote !== fav.path) $el.style.backgroundColor = 'var(--bg-hover)'"
|
||||||
@mouseleave="if (currentNote !== fav.path) $el.style.backgroundColor = 'transparent'"
|
@mouseleave="if (currentNote !== fav.path) $el.style.backgroundColor = 'transparent'"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue