From f44a5402245e8be768fc8729301b731072f9243f Mon Sep 17 00:00:00 2001 From: sudo-Harshk Date: Thu, 20 Nov 2025 08:31:27 +0530 Subject: [PATCH 1/3] feat: replace welcome screen with responsive homepage grid view --- frontend/app.js | 144 ++++++++++++++++++++++++++++++++++++++++- frontend/index.html | 153 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 278 insertions(+), 19 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index 849b33f..6a61baa 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -92,6 +92,108 @@ function noteApp() { // Dropdown state showNewDropdown: false, + // Homepage folder navigation + selectedHomepageFolder: '', + + // Computed properties for homepage (using getters for Alpine reactivity) + get homepageNotes() { + // Safety check - ensure notes is an array + if (!Array.isArray(this.notes)) { + return []; + } + + // Filter notes based on selected folder + if (!this.selectedHomepageFolder) { + // Root level - show notes without folder + return this.notes.filter(note => !note.folder); + } else { + // Show notes in selected folder (exact match) + return this.notes.filter(note => note.folder === this.selectedHomepageFolder); + } + }, + + get homepageFolders() { + // Safety check - ensure allFolders is an array + if (!Array.isArray(this.allFolders)) { + return []; + } + + // Safety check - ensure notes is an array for counting + const notesArray = Array.isArray(this.notes) ? this.notes : []; + + // Get folders for current view + if (!this.selectedHomepageFolder) { + // Root level - show top-level folders + return this.allFolders + .filter(folder => !folder.includes('/')) + .map(folder => { + // Count notes in this folder and all subfolders + const noteCount = notesArray.filter(note => + note.folder === folder || (note.folder && note.folder.startsWith(folder + '/')) + ).length; + + return { + name: folder, + path: folder, + noteCount: noteCount + }; + }); + } else { + // Get subfolders of current folder + const prefix = this.selectedHomepageFolder + '/'; + const subfolders = this.allFolders + .filter(folder => folder.startsWith(prefix) && folder !== this.selectedHomepageFolder) + .map(folder => { + // Extract subfolder name (next level after current folder) + const relativePath = folder.substring(this.selectedHomepageFolder.length + 1); + const subfolderName = relativePath.split('/')[0]; + const subfolderPath = this.selectedHomepageFolder + '/' + subfolderName; + + // Count notes in this subfolder and all nested subfolders + const noteCount = notesArray.filter(note => + note.folder === subfolderPath || (note.folder && note.folder.startsWith(subfolderPath + '/')) + ).length; + + return { + name: subfolderName, + path: subfolderPath, + noteCount: noteCount + }; + }) + .filter((folder, index, self) => + // Remove duplicates (same subfolder name) + index === self.findIndex(f => f.path === folder.path) + ); + + return subfolders; + } + }, + + get homepageBreadcrumb() { + // Always return at least Home breadcrumb + // Safety check - ensure we always return an array + try { + const breadcrumb = [{ name: 'Home', path: '' }]; + + if (!this.selectedHomepageFolder) { + return breadcrumb; + } + + const parts = (this.selectedHomepageFolder || '').split('/').filter(part => part.trim() !== ''); + + let currentPath = ''; + parts.forEach(part => { + currentPath = currentPath ? currentPath + '/' + part : part; + breadcrumb.push({ name: part, path: currentPath }); + }); + + return breadcrumb; + } catch (error) { + // Fallback to just Home if anything goes wrong + return [{ name: 'Home', path: '' }]; + } + }, + // Mermaid state cache lastMermaidTheme: null, @@ -116,9 +218,15 @@ function noteApp() { // Parse URL and load specific note if provided this.loadNoteFromURL(); + // Set initial homepage state if no note is loaded + if (!this.currentNote) { + window.history.replaceState({ homepageFolder: '' }, '', '/'); + } + // Listen for browser back/forward navigation window.addEventListener('popstate', (e) => { if (e.state && e.state.notePath) { + // Navigating to a note const searchQuery = e.state.searchQuery || ''; this.loadNote(e.state.notePath, false, searchQuery); // false = don't update history @@ -131,6 +239,24 @@ function noteApp() { this.searchResults = []; this.clearSearchHighlights(); } + } else { + // Navigating back to homepage + this.currentNote = ''; + this.noteContent = ''; + this.currentNoteName = ''; + + // Restore homepage folder state if it was saved + if (e.state && e.state.homepageFolder !== undefined) { + this.selectedHomepageFolder = e.state.homepageFolder || ''; + } else { + // No folder state in history, go to root + this.selectedHomepageFolder = ''; + } + + // Clear search + this.searchQuery = ''; + this.searchResults = []; + this.clearSearchHighlights(); } }); @@ -916,7 +1042,7 @@ function noteApp() { if (!response.ok) { if (response.status === 404) { // Note not found - silently redirect to home - window.history.replaceState({}, '', '/'); + window.history.replaceState({ homepageFolder: this.selectedHomepageFolder || '' }, '', '/'); this.currentNote = ''; this.noteContent = ''; return; @@ -947,7 +1073,11 @@ function noteApp() { url += `?search=${encodeURIComponent(searchQuery)}`; } window.history.pushState( - { notePath: notePath, searchQuery: searchQuery }, + { + notePath: notePath, + searchQuery: searchQuery, + homepageFolder: this.selectedHomepageFolder || '' // Save current folder state + }, '', url ); @@ -2550,6 +2680,16 @@ function noteApp() { console.error('HTML export failed:', error); alert(`Failed to export HTML: ${error.message}`); } + }, + + // Homepage folder navigation methods + goToHomepageFolder(folderPath) { + // Navigate to folder (empty string = root) + this.selectedHomepageFolder = folderPath || ''; + + // Update browser history + const state = { homepageFolder: folderPath || '' }; + window.history.pushState(state, '', '/'); } } } diff --git a/frontend/index.html b/frontend/index.html index f52dce6..e7d5949 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -997,23 +997,142 @@

From 5821e6f9ec65b067fb02495369052c5cabd7dc9f Mon Sep 17 00:00:00 2001 From: sudo-Harshk Date: Thu, 20 Nov 2025 20:01:36 +0530 Subject: [PATCH 2/3] refactor: enhance homepage functionality with improved folder and note handling - Added file size formatting for notes and improved breadcrumb navigation. --- frontend/app.js | 156 +++++++++++++++---------- frontend/index.html | 275 ++++++++++++++++++++++++-------------------- 2 files changed, 246 insertions(+), 185 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index 6a61baa..71dca87 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -95,81 +95,64 @@ function noteApp() { // Homepage folder navigation selectedHomepageFolder: '', - // Computed properties for homepage (using getters for Alpine reactivity) - get homepageNotes() { - // Safety check - ensure notes is an array - if (!Array.isArray(this.notes)) { + // Computed-like helpers for homepage (use methods for safer evaluation) + homepageNotes() { + if (!this.folderTree || typeof this.folderTree !== 'object') { return []; } - // Filter notes based on selected folder - if (!this.selectedHomepageFolder) { - // Root level - show notes without folder - return this.notes.filter(note => !note.folder); - } else { - // Show notes in selected folder (exact match) - return this.notes.filter(note => note.folder === this.selectedHomepageFolder); + const folderNode = this.getFolderNode(this.selectedHomepageFolder || ''); + if (!folderNode || !Array.isArray(folderNode.notes)) { + return []; } + + return folderNode.notes; + }, + + // Helper: Format file size nicely + formatSize(bytes) { + if (bytes === 0 || !bytes) return '0 B'; + + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; }, - get homepageFolders() { - // Safety check - ensure allFolders is an array - if (!Array.isArray(this.allFolders)) { + homepageFolders() { + if (!this.folderTree || typeof this.folderTree !== 'object') { return []; } - // Safety check - ensure notes is an array for counting - const notesArray = Array.isArray(this.notes) ? this.notes : []; - - // Get folders for current view + let childFolders = []; if (!this.selectedHomepageFolder) { - // Root level - show top-level folders - return this.allFolders - .filter(folder => !folder.includes('/')) - .map(folder => { - // Count notes in this folder and all subfolders - const noteCount = notesArray.filter(note => - note.folder === folder || (note.folder && note.folder.startsWith(folder + '/')) - ).length; - - return { - name: folder, - path: folder, - noteCount: noteCount - }; - }); + childFolders = Object.entries(this.folderTree) + .filter(([key]) => key !== '__root__') + .map(([, folder]) => folder); } else { - // Get subfolders of current folder - const prefix = this.selectedHomepageFolder + '/'; - const subfolders = this.allFolders - .filter(folder => folder.startsWith(prefix) && folder !== this.selectedHomepageFolder) - .map(folder => { - // Extract subfolder name (next level after current folder) - const relativePath = folder.substring(this.selectedHomepageFolder.length + 1); - const subfolderName = relativePath.split('/')[0]; - const subfolderPath = this.selectedHomepageFolder + '/' + subfolderName; - - // Count notes in this subfolder and all nested subfolders - const noteCount = notesArray.filter(note => - note.folder === subfolderPath || (note.folder && note.folder.startsWith(subfolderPath + '/')) - ).length; - - return { - name: subfolderName, - path: subfolderPath, - noteCount: noteCount - }; - }) - .filter((folder, index, self) => - // Remove duplicates (same subfolder name) - index === self.findIndex(f => f.path === folder.path) - ); - - return subfolders; + const parentFolder = this.getFolderNode(this.selectedHomepageFolder); + if (!parentFolder || !parentFolder.children) { + return []; + } + childFolders = Object.values(parentFolder.children); + } + + if (!Array.isArray(childFolders) || childFolders.length === 0) { + return []; } + + return childFolders + .map(folder => ({ + name: folder.name, + path: folder.path, + noteCount: this.calculateFolderNoteCount(folder) + })) + .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())); }, - get homepageBreadcrumb() { + homepageBreadcrumb() { // Always return at least Home breadcrumb // Safety check - ensure we always return an array try { @@ -192,6 +175,56 @@ function noteApp() { // Fallback to just Home if anything goes wrong return [{ name: 'Home', path: '' }]; } + }, + + getFolderNode(folderPath = '') { + if (!this.folderTree || typeof this.folderTree !== 'object') { + return null; + } + + if (!folderPath) { + if (this.folderTree['__root__']) { + return this.folderTree['__root__']; + } + return { name: '', path: '', children: {}, notes: [] }; + } + + const parts = folderPath.split('/').filter(Boolean); + let currentLevel = this.folderTree; + let node = null; + + for (const part of parts) { + if (!currentLevel[part]) { + return null; + } + node = currentLevel[part]; + currentLevel = node.children || {}; + } + + return node; + }, + + calculateFolderNoteCount(folderNode) { + if (!folderNode || typeof folderNode !== 'object') { + return 0; + } + + const directNotes = Array.isArray(folderNode.notes) ? folderNode.notes.length : 0; + if (!folderNode.children || Object.keys(folderNode.children).length === 0) { + return directNotes; + } + + return Object.values(folderNode.children).reduce( + (total, child) => total + this.calculateFolderNoteCount(child), + directNotes + ); + }, + + // Check if app is empty (no notes and no folders) + get isAppEmpty() { + const notesArray = Array.isArray(this.notes) ? this.notes : []; + const foldersArray = Array.isArray(this.allFolders) ? this.allFolders : []; + return notesArray.length === 0 && foldersArray.length === 0; }, // Mermaid state cache @@ -1676,6 +1709,7 @@ function noteApp() { const note = this.notes.find(n => n.path === this.currentNote); if (note) { note.modified = new Date().toISOString(); + note.size = new Blob([this.noteContent]).size; } // Hide "saved" indicator diff --git a/frontend/index.html b/frontend/index.html index e7d5949..6822d6e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -720,10 +720,15 @@ >
-
+
+
@@ -992,148 +997,170 @@

-