Merge pull request #48 from gamosoft/features/unified-create

Features/unified create
This commit is contained in:
Gamosoft 2025-11-27 14:14:37 +01:00 committed by GitHub
commit 88c82e3965
2 changed files with 87 additions and 27 deletions

View File

@ -99,7 +99,7 @@ function noteApp() {
// Dropdown state // Dropdown state
showNewDropdown: false, showNewDropdown: false,
dropdownTargetFolder: '', // Folder context for "New" dropdown (empty = root) dropdownTargetFolder: null, // Folder context for "New" dropdown ('' = root, null = not set)
dropdownPosition: { top: 0, left: 0 }, // Position for contextual dropdown dropdownPosition: { top: 0, left: 0 }, // Position for contextual dropdown
// Template state // Template state
@ -628,9 +628,16 @@ function noteApp() {
notePath += '.md'; notePath += '.md';
} }
// If we have a target folder context, create in that folder // Determine target folder: use dropdown context if set, otherwise homepage folder
if (this.dropdownTargetFolder || this.selectedHomepageFolder) { let targetFolder;
const targetFolder = this.dropdownTargetFolder || this.selectedHomepageFolder; if (this.dropdownTargetFolder !== null && this.dropdownTargetFolder !== undefined) {
targetFolder = this.dropdownTargetFolder; // Can be '' for root or a folder path
} else {
targetFolder = this.selectedHomepageFolder || '';
}
// If we have a target folder, create note in that folder
if (targetFolder) {
notePath = `${targetFolder}/${notePath}`; notePath = `${targetFolder}/${notePath}`;
} }
@ -1998,7 +2005,7 @@ function noteApp() {
closeDropdown() { closeDropdown() {
this.showNewDropdown = false; this.showNewDropdown = false;
this.dropdownTargetFolder = ''; // Reset folder context this.dropdownTargetFolder = null; // Reset folder context
}, },
// ===================================================== // =====================================================
@ -2006,8 +2013,16 @@ function noteApp() {
// ===================================================== // =====================================================
async createNote(folderPath = null) { async createNote(folderPath = null) {
// Use provided folder path, or dropdown target folder context // Use provided folder path, or dropdown target folder context, or homepage folder
const targetFolder = folderPath !== null ? folderPath : this.dropdownTargetFolder; // Note: Check dropdownTargetFolder !== null to distinguish between '' (root) and not set
let targetFolder;
if (folderPath !== null) {
targetFolder = folderPath;
} else if (this.dropdownTargetFolder !== null && this.dropdownTargetFolder !== undefined) {
targetFolder = this.dropdownTargetFolder; // Can be '' for root or a folder path
} else {
targetFolder = this.selectedHomepageFolder || '';
}
this.closeDropdown(); this.closeDropdown();
const promptText = targetFolder const promptText = targetFolder
@ -2059,8 +2074,16 @@ function noteApp() {
}, },
async createFolder(parentPath = null) { async createFolder(parentPath = null) {
// Use provided parent path, or dropdown target folder context // Use provided parent path, or dropdown target folder context, or homepage folder
const targetFolder = parentPath !== null ? parentPath : this.dropdownTargetFolder; // Note: Check dropdownTargetFolder !== null to distinguish between '' (root) and not set
let targetFolder;
if (parentPath !== null) {
targetFolder = parentPath;
} else if (this.dropdownTargetFolder !== null && this.dropdownTargetFolder !== undefined) {
targetFolder = this.dropdownTargetFolder; // Can be '' for root or a folder path
} else {
targetFolder = this.selectedHomepageFolder || '';
}
this.closeDropdown(); this.closeDropdown();
const promptText = targetFolder const promptText = targetFolder
@ -2098,6 +2121,9 @@ function noteApp() {
} }
this.expandedFolders.add(folderPath); this.expandedFolders.add(folderPath);
await this.loadNotes(); await this.loadNotes();
// Navigate to the newly created folder on the homepage
this.goToHomepageFolder(folderPath);
} else { } else {
ErrorHandler.handle('create folder', new Error('Server returned error')); ErrorHandler.handle('create folder', new Error('Server returned error'));
} }
@ -3397,6 +3423,12 @@ function noteApp() {
goToHomepageFolder(folderPath) { goToHomepageFolder(folderPath) {
this.selectedHomepageFolder = folderPath || ''; this.selectedHomepageFolder = folderPath || '';
// Clear editor state to show landing page
this.currentNote = '';
this.currentNoteName = '';
this.noteContent = '';
this.currentImage = '';
// Invalidate cache to force recalculation // Invalidate cache to force recalculation
this._homepageCache = { this._homepageCache = {
folderPath: null, folderPath: null,

View File

@ -1129,19 +1129,19 @@
<!-- Welcome Hero - Show when app is empty --> <!-- Welcome Hero - Show when app is empty -->
<template x-if="isAppEmpty"> <template x-if="isAppEmpty">
<div class="flex flex-col items-center justify-center h-full py-16 text-center"> <div class="flex flex-col items-center justify-center h-full py-16 text-center">
<svg class="mx-auto h-16 w-16 mb-4" style="color: var(--text-tertiary);" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="mx-auto h-24 w-24 mb-4" style="color: var(--text-tertiary); opacity: 0.5;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg> </svg>
<h1 class="text-4xl font-bold mb-4" style="color: var(--text-primary);" x-text="appName"></h1> <h2 class="text-2xl font-bold mb-2" style="color: var(--text-primary);">No notes yet</h2>
<p class="text-xl mb-8" style="color: var(--text-secondary);" x-text="appTagline"></p> <p class="mb-6" style="color: var(--text-secondary);">Create your first note or folder</p>
<button <button
@click="createNote()" @click="dropdownTargetFolder = selectedHomepageFolder; toggleNewDropdown($event)"
class="px-8 py-4 text-base font-medium text-white rounded-lg transition-colors" class="px-6 py-3 text-sm font-medium text-white rounded-lg transition-colors"
style="background-color: var(--accent-primary);" style="background-color: var(--accent-primary);"
onmouseover="this.style.backgroundColor='var(--accent-hover)'" onmouseover="this.style.backgroundColor='var(--accent-hover)'"
onmouseout="this.style.backgroundColor='var(--accent-primary)'" onmouseout="this.style.backgroundColor='var(--accent-primary)'"
> >
Create Your First Note + New
</button> </button>
</div> </div>
</template> </template>
@ -1196,13 +1196,13 @@
<span x-text="homepageFolders().length === 1 ? 'folder' : 'folders'"></span> <span x-text="homepageFolders().length === 1 ? 'folder' : 'folders'"></span>
</span> </span>
<button <button
@click="createNote()" @click="dropdownTargetFolder = selectedHomepageFolder; toggleNewDropdown($event)"
class="ml-auto px-4 py-2 text-sm font-medium text-white rounded-lg transition-colors" class="ml-auto px-4 py-2 text-sm font-medium text-white rounded-lg transition-colors"
style="background-color: var(--accent-primary);" style="background-color: var(--accent-primary);"
onmouseover="this.style.backgroundColor='var(--accent-hover)'" onmouseover="this.style.backgroundColor='var(--accent-hover)'"
onmouseout="this.style.backgroundColor='var(--accent-primary)'" onmouseout="this.style.backgroundColor='var(--accent-primary)'"
> >
+ New Note + New
</button> </button>
</div> </div>
</div> </div>
@ -1214,16 +1214,16 @@
<svg class="mx-auto h-24 w-24 mb-4" style="color: var(--text-tertiary); opacity: 0.5;" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="mx-auto h-24 w-24 mb-4" style="color: var(--text-tertiary); opacity: 0.5;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg> </svg>
<h2 class="text-2xl font-bold mb-2" style="color: var(--text-primary);">No notes yet</h2> <h2 class="text-2xl font-bold mb-2" style="color: var(--text-primary);" x-text="selectedHomepageFolder ? 'This folder is empty' : 'No notes yet'"></h2>
<p class="mb-6" style="color: var(--text-secondary);">Create your first note to get started</p> <p class="mb-6" style="color: var(--text-secondary);" x-text="selectedHomepageFolder ? 'Create something to get started' : 'Create your first note or folder'"></p>
<button <button
@click="createNote()" @click="dropdownTargetFolder = selectedHomepageFolder; toggleNewDropdown($event)"
class="px-6 py-3 text-sm font-medium text-white rounded-lg transition-colors" class="px-6 py-3 text-sm font-medium text-white rounded-lg transition-colors"
style="background-color: var(--accent-primary);" style="background-color: var(--accent-primary);"
onmouseover="this.style.backgroundColor='var(--accent-hover)'" onmouseover="this.style.backgroundColor='var(--accent-hover)'"
onmouseout="this.style.backgroundColor='var(--accent-primary)'" onmouseout="this.style.backgroundColor='var(--accent-primary)'"
> >
Create Your First Note + New
</button> </button>
</div> </div>
</template> </template>
@ -1236,10 +1236,24 @@
<div <div
@click="goToHomepageFolder(folder.path)" @click="goToHomepageFolder(folder.path)"
class="p-4 rounded-lg border-2 cursor-pointer transition-all hover:shadow-lg" class="p-4 rounded-lg border-2 cursor-pointer transition-all hover:shadow-lg"
style="background-color: var(--bg-secondary); border-color: var(--accent-primary); min-height: 140px; display: flex; flex-direction: column; border-width: 2px;" style="background-color: var(--bg-secondary); border-color: var(--accent-primary); min-height: 140px; display: flex; flex-direction: column; border-width: 2px; position: relative;"
onmouseover="this.style.borderColor='var(--accent-hover)'; this.style.transform='translateY(-2px)'" onmouseover="this.style.borderColor='var(--accent-hover)'; this.style.transform='translateY(-2px)'; this.querySelector('.card-delete-btn').style.opacity='1';"
onmouseout="this.style.borderColor='var(--accent-primary)'; this.style.transform='translateY(0)'" onmouseout="this.style.borderColor='var(--accent-primary)'; this.style.transform='translateY(0)'; this.querySelector('.card-delete-btn').style.opacity='0';"
> >
<!-- Delete Button -->
<button
@click.stop="deleteFolder(folder.path, folder.name)"
class="card-delete-btn"
style="position: absolute; top: 8px; right: 8px; opacity: 0; transition: opacity 0.2s; color: var(--error); padding: 4px; border-radius: 4px; background-color: var(--bg-secondary);"
onmouseover="this.style.backgroundColor='var(--bg-hover)'"
onmouseout="this.style.backgroundColor='var(--bg-secondary)'"
title="Delete folder"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</button>
<!-- Folder Icon --> <!-- Folder Icon -->
<div class="mb-2 flex items-center gap-2"> <div class="mb-2 flex items-center gap-2">
<svg class="w-6 h-6" style="color: var(--accent-primary);" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-6 h-6" style="color: var(--accent-primary);" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@ -1263,10 +1277,24 @@
<div <div
@click="note.path.match(/\.(png|jpg|jpeg|gif|webp)$/i) ? (currentImage = note.path, currentNote = '') : loadNote(note.path)" @click="note.path.match(/\.(png|jpg|jpeg|gif|webp)$/i) ? (currentImage = note.path, currentNote = '') : loadNote(note.path)"
class="p-4 rounded-lg border-2 cursor-pointer transition-all hover:shadow-lg" class="p-4 rounded-lg border-2 cursor-pointer transition-all hover:shadow-lg"
style="background-color: var(--bg-secondary); border-color: var(--border-primary); min-height: 140px; display: flex; flex-direction: column;" style="background-color: var(--bg-secondary); border-color: var(--border-primary); min-height: 140px; display: flex; flex-direction: column; position: relative;"
onmouseover="this.style.borderColor='var(--accent-primary)'; this.style.transform='translateY(-2px)'" onmouseover="this.style.borderColor='var(--accent-primary)'; this.style.transform='translateY(-2px)'; this.querySelector('.card-delete-btn').style.opacity='1';"
onmouseout="this.style.borderColor='var(--border-primary)'; this.style.transform='translateY(0)'" onmouseout="this.style.borderColor='var(--border-primary)'; this.style.transform='translateY(0)'; this.querySelector('.card-delete-btn').style.opacity='0';"
> >
<!-- Delete Button -->
<button
@click.stop="note.type === 'image' ? deleteImage(note.path) : deleteNote(note.path, note.name)"
class="card-delete-btn"
style="position: absolute; top: 8px; right: 8px; opacity: 0; transition: opacity 0.2s; color: var(--error); padding: 4px; border-radius: 4px; background-color: var(--bg-secondary);"
onmouseover="this.style.backgroundColor='var(--bg-hover)'"
onmouseout="this.style.backgroundColor='var(--bg-secondary)'"
:title="note.type === 'image' ? 'Delete image' : 'Delete note'"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</button>
<!-- Note Title --> <!-- Note Title -->
<h3 class="font-semibold text-base mb-2" style="color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical;" x-text="note.name"></h3> <h3 class="font-semibold text-base mb-2" style="color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical;" x-text="note.name"></h3>