Last active
February 1, 2026 21:21
-
-
Save mdigan/56f431a55a1cf7a070009427c09e74be to your computer and use it in GitHub Desktop.
My Personal RSS Reader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Notes</title> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| :root { | |
| --bg: #ffffff; | |
| --sidebar-bg: #fafafa; | |
| --text: #1a1a1a; | |
| --meta: #666; | |
| --accent: #0066cc; | |
| --border: transparent; | |
| --hover: #f0f0f0; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --bg: #121212; | |
| --sidebar-bg: #1a1a1a; | |
| --text: #e0e0e0; | |
| --meta: #999; | |
| --accent: #66b2ff; | |
| --border: transparent; | |
| --hover: #2a2a2a; | |
| } | |
| } | |
| * { box-sizing: border-box; } | |
| body, html { | |
| margin: 0; | |
| padding: 0; | |
| height: 100%; | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| background: var(--bg); | |
| color: var(--text); | |
| overflow: hidden; | |
| } | |
| /* App Container */ | |
| .app-container { | |
| display: flex; | |
| justify-content: center; | |
| height: 100vh; | |
| width: 100%; | |
| } | |
| /* Sidebar removed, main content is now the editor */ | |
| .editor-container { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| padding: 2rem; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| } | |
| #note-body { | |
| flex: 1; | |
| border: none; | |
| background: transparent; | |
| color: var(--text); | |
| outline: none; | |
| font-size: 1.1rem; | |
| line-height: 1.7; | |
| resize: none; | |
| width: 100%; | |
| padding-top: 1rem; | |
| display: none; /* Toggle with preview */ | |
| } | |
| #note-preview { | |
| flex: 1; | |
| overflow-y: auto; | |
| font-size: 1.1rem; | |
| line-height: 1.7; | |
| color: var(--text); | |
| } | |
| #note-preview h1, #note-preview h2, #note-preview h3 { margin-top: 1.5rem; } | |
| #note-preview code { background: var(--hover); padding: 0.2rem 0.4rem; font-size: 0.9em; } | |
| #note-preview pre { background: var(--hover); padding: 1rem; overflow-x: auto; } | |
| #note-preview ul, #note-preview ol { padding-left: 1.5rem; } | |
| #note-preview blockquote { border-left: 4px solid var(--accent); margin: 0; padding-left: 1rem; color: var(--meta); } | |
| #note-preview img { max-width: 100%; } | |
| .editor-actions { | |
| display: flex; | |
| justify-content: flex-end; | |
| padding: 1rem 0; | |
| gap: 1rem; | |
| align-items: center; | |
| } | |
| .mode-toggle { | |
| font-size: 0.8rem; | |
| color: var(--meta); | |
| cursor: pointer; | |
| text-decoration: underline; | |
| } | |
| .btn-delete { | |
| background: transparent; | |
| color: #ff4d4d; | |
| border: 1px solid #ff4d4d; | |
| padding: 0.4rem 0.8rem; | |
| cursor: pointer; | |
| font-size: 0.8rem; | |
| } | |
| .btn-delete:hover { | |
| background: #ff4d4d; | |
| color: white; | |
| } | |
| /* Empty State */ | |
| .empty-state { | |
| flex: 1; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--meta); | |
| font-style: italic; | |
| } | |
| ::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--meta); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <div class="editor-container" id="editor-container"> | |
| <div class="editor-actions"> | |
| <span class="mode-toggle" id="mode-toggle" onclick="toggleMode()">Switch to Preview (Ctrl+P)</span> | |
| <button class="btn-delete" onclick="deleteCurrentNote()">Delete Note</button> | |
| </div> | |
| <textarea id="note-body" placeholder="Start writing..."></textarea> | |
| <div id="note-preview" style="display: none;"></div> | |
| </div> | |
| </div> | |
| <script> | |
| /** | |
| * Notes App Logic | |
| */ | |
| (function() { | |
| 'use strict'; | |
| let notes = JSON.parse(localStorage.getItem('notes') || '[]'); | |
| let currentNoteId = null; | |
| let isEditing = true; | |
| const bodyInput = document.getElementById('note-body'); | |
| const previewEl = document.getElementById('note-preview'); | |
| const modeToggle = document.getElementById('mode-toggle'); | |
| /** | |
| * Persistence | |
| */ | |
| function saveToLocalStorage() { | |
| localStorage.setItem('notes', JSON.stringify(notes)); | |
| } | |
| /** | |
| * UI Actions | |
| */ | |
| window.toggleMode = function() { | |
| isEditing = !isEditing; | |
| if (isEditing) { | |
| bodyInput.style.display = 'block'; | |
| previewEl.style.display = 'none'; | |
| modeToggle.textContent = 'Switch to Preview (Ctrl+P)'; | |
| bodyInput.focus(); | |
| } else { | |
| bodyInput.style.display = 'none'; | |
| previewEl.style.display = 'block'; | |
| modeToggle.textContent = 'Switch to Edit (Ctrl+P)'; | |
| renderPreview(); | |
| } | |
| }; | |
| function renderPreview() { | |
| if (window.marked && typeof window.marked.parse === 'function') { | |
| previewEl.innerHTML = window.marked.parse(bodyInput.value); | |
| } else { | |
| previewEl.textContent = bodyInput.value; | |
| } | |
| } | |
| window.createNewNote = function() { | |
| const newNote = { | |
| id: Date.now().toString(), | |
| content: '', | |
| lastModified: Date.now() | |
| }; | |
| notes.unshift(newNote); | |
| saveToLocalStorage(); | |
| isEditing = true; | |
| selectNote(newNote.id); | |
| bodyInput.focus(); | |
| }; | |
| function navigateNotes(direction) { | |
| if (notes.length <= 1) return; | |
| let currentIndex = notes.findIndex(n => n.id === currentNoteId); | |
| if (currentIndex === -1) currentIndex = 0; | |
| let nextIndex; | |
| if (direction === 'next') { | |
| nextIndex = (currentIndex + 1) % notes.length; | |
| } else { | |
| nextIndex = (currentIndex - 1 + notes.length) % notes.length; | |
| } | |
| selectNote(notes[nextIndex].id); | |
| } | |
| function selectNote(id) { | |
| currentNoteId = id; | |
| const note = notes.find(n => n.id === id); | |
| if (note) { | |
| bodyInput.value = note.content; | |
| if (!isEditing) { | |
| renderPreview(); | |
| } else { | |
| bodyInput.style.display = 'block'; | |
| previewEl.style.display = 'none'; | |
| modeToggle.textContent = 'Switch to Preview (Ctrl+P)'; | |
| } | |
| } | |
| } | |
| function updateNote() { | |
| if (!currentNoteId) return; | |
| const noteIndex = notes.findIndex(n => n.id === currentNoteId); | |
| if (noteIndex !== -1) { | |
| notes[noteIndex].content = bodyInput.value; | |
| notes[noteIndex].lastModified = Date.now(); | |
| saveToLocalStorage(); | |
| } | |
| } | |
| window.deleteCurrentNote = function() { | |
| if (!currentNoteId) return; | |
| if (window.confirm('Are you sure you want to delete this note?')) { | |
| notes = notes.filter(n => n.id !== currentNoteId); | |
| currentNoteId = null; | |
| saveToLocalStorage(); | |
| if (notes.length > 0) { | |
| selectNote(notes[0].id); | |
| } else { | |
| window.createNewNote(); | |
| } | |
| } | |
| }; | |
| /** | |
| * Events | |
| */ | |
| bodyInput.addEventListener('input', updateNote); | |
| window.addEventListener('keydown', (e) => { | |
| const isCtrl = e.ctrlKey || e.metaKey; // Support Cmd on Mac too | |
| if (isCtrl && e.key === 'n') { | |
| e.preventDefault(); | |
| window.createNewNote(); | |
| } else if (isCtrl && e.key === 'j') { | |
| e.preventDefault(); | |
| navigateNotes('prev'); | |
| } else if (isCtrl && e.key === 'k') { | |
| e.preventDefault(); | |
| navigateNotes('next'); | |
| } else if (isCtrl && e.key === 'p') { | |
| e.preventDefault(); | |
| window.toggleMode(); | |
| } | |
| }); | |
| /** | |
| * Init | |
| */ | |
| if (notes.length > 0) { | |
| selectNote(notes[0].id); | |
| } else { | |
| window.createNewNote(); | |
| } | |
| })(); | |
| </script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Notes</title> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| :root { | |
| --bg: #ffffff; | |
| --sidebar-bg: #fafafa; | |
| --text: #1a1a1a; | |
| --meta: #666; | |
| --accent: #0066cc; | |
| --border: transparent; | |
| --hover: #f0f0f0; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --bg: #121212; | |
| --sidebar-bg: #1a1a1a; | |
| --text: #e0e0e0; | |
| --meta: #999; | |
| --accent: #66b2ff; | |
| --border: transparent; | |
| --hover: #2a2a2a; | |
| } | |
| } | |
| * { box-sizing: border-box; } | |
| body, html { | |
| margin: 0; | |
| padding: 0; | |
| height: 100%; | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| background: var(--bg); | |
| color: var(--text); | |
| overflow: hidden; | |
| } | |
| /* App Container */ | |
| .app-container { | |
| display: flex; | |
| justify-content: center; | |
| height: 100vh; | |
| width: 100%; | |
| } | |
| /* Sidebar removed, main content is now the editor */ | |
| .editor-container { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| padding: 2rem; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| } | |
| #note-body { | |
| flex: 1; | |
| border: none; | |
| background: transparent; | |
| color: var(--text); | |
| outline: none; | |
| font-size: 1.1rem; | |
| line-height: 1.7; | |
| resize: none; | |
| width: 100%; | |
| padding-top: 1rem; | |
| display: none; /* Toggle with preview */ | |
| } | |
| #note-preview { | |
| flex: 1; | |
| overflow-y: auto; | |
| font-size: 1.1rem; | |
| line-height: 1.7; | |
| color: var(--text); | |
| } | |
| #note-preview h1, #note-preview h2, #note-preview h3 { margin-top: 1.5rem; } | |
| #note-preview code { background: var(--hover); padding: 0.2rem 0.4rem; font-size: 0.9em; } | |
| #note-preview pre { background: var(--hover); padding: 1rem; overflow-x: auto; } | |
| #note-preview ul, #note-preview ol { padding-left: 1.5rem; } | |
| #note-preview blockquote { border-left: 4px solid var(--accent); margin: 0; padding-left: 1rem; color: var(--meta); } | |
| #note-preview img { max-width: 100%; } | |
| .editor-actions { | |
| display: flex; | |
| justify-content: flex-end; | |
| padding: 1rem 0; | |
| gap: 1rem; | |
| align-items: center; | |
| } | |
| .mode-toggle { | |
| font-size: 0.8rem; | |
| color: var(--meta); | |
| cursor: pointer; | |
| text-decoration: underline; | |
| } | |
| .btn-delete { | |
| background: transparent; | |
| color: #ff4d4d; | |
| border: 1px solid #ff4d4d; | |
| padding: 0.4rem 0.8rem; | |
| cursor: pointer; | |
| font-size: 0.8rem; | |
| } | |
| .btn-delete:hover { | |
| background: #ff4d4d; | |
| color: white; | |
| } | |
| /* Empty State */ | |
| .empty-state { | |
| flex: 1; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--meta); | |
| font-style: italic; | |
| } | |
| ::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--meta); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <div class="editor-container" id="editor-container"> | |
| <div class="editor-actions"> | |
| <span class="mode-toggle" id="mode-toggle" onclick="toggleMode()">Switch to Preview (Ctrl+P)</span> | |
| <button class="btn-delete" onclick="deleteCurrentNote()">Delete Note</button> | |
| </div> | |
| <textarea id="note-body" placeholder="Start writing..."></textarea> | |
| <div id="note-preview" style="display: none;"></div> | |
| </div> | |
| </div> | |
| <script> | |
| /** | |
| * Notes App Logic | |
| */ | |
| (function() { | |
| 'use strict'; | |
| let notes = JSON.parse(localStorage.getItem('notes') || '[]'); | |
| let currentNoteId = null; | |
| let isEditing = true; | |
| const bodyInput = document.getElementById('note-body'); | |
| const previewEl = document.getElementById('note-preview'); | |
| const modeToggle = document.getElementById('mode-toggle'); | |
| /** | |
| * Persistence | |
| */ | |
| function saveToLocalStorage() { | |
| localStorage.setItem('notes', JSON.stringify(notes)); | |
| } | |
| /** | |
| * UI Actions | |
| */ | |
| window.toggleMode = function() { | |
| isEditing = !isEditing; | |
| if (isEditing) { | |
| bodyInput.style.display = 'block'; | |
| previewEl.style.display = 'none'; | |
| modeToggle.textContent = 'Switch to Preview (Ctrl+P)'; | |
| bodyInput.focus(); | |
| } else { | |
| bodyInput.style.display = 'none'; | |
| previewEl.style.display = 'block'; | |
| modeToggle.textContent = 'Switch to Edit (Ctrl+P)'; | |
| renderPreview(); | |
| } | |
| }; | |
| function renderPreview() { | |
| if (window.marked && typeof window.marked.parse === 'function') { | |
| previewEl.innerHTML = window.marked.parse(bodyInput.value); | |
| } else { | |
| previewEl.textContent = bodyInput.value; | |
| } | |
| } | |
| window.createNewNote = function() { | |
| const newNote = { | |
| id: Date.now().toString(), | |
| content: '', | |
| lastModified: Date.now() | |
| }; | |
| notes.unshift(newNote); | |
| saveToLocalStorage(); | |
| isEditing = true; | |
| selectNote(newNote.id); | |
| bodyInput.focus(); | |
| }; | |
| function navigateNotes(direction) { | |
| if (notes.length <= 1) return; | |
| let currentIndex = notes.findIndex(n => n.id === currentNoteId); | |
| if (currentIndex === -1) currentIndex = 0; | |
| let nextIndex; | |
| if (direction === 'next') { | |
| nextIndex = (currentIndex + 1) % notes.length; | |
| } else { | |
| nextIndex = (currentIndex - 1 + notes.length) % notes.length; | |
| } | |
| selectNote(notes[nextIndex].id); | |
| } | |
| function selectNote(id) { | |
| currentNoteId = id; | |
| const note = notes.find(n => n.id === id); | |
| if (note) { | |
| bodyInput.value = note.content; | |
| if (!isEditing) { | |
| renderPreview(); | |
| } else { | |
| bodyInput.style.display = 'block'; | |
| previewEl.style.display = 'none'; | |
| modeToggle.textContent = 'Switch to Preview (Ctrl+P)'; | |
| } | |
| } | |
| } | |
| function updateNote() { | |
| if (!currentNoteId) return; | |
| const noteIndex = notes.findIndex(n => n.id === currentNoteId); | |
| if (noteIndex !== -1) { | |
| notes[noteIndex].content = bodyInput.value; | |
| notes[noteIndex].lastModified = Date.now(); | |
| saveToLocalStorage(); | |
| } | |
| } | |
| window.deleteCurrentNote = function() { | |
| if (!currentNoteId) return; | |
| if (window.confirm('Are you sure you want to delete this note?')) { | |
| notes = notes.filter(n => n.id !== currentNoteId); | |
| currentNoteId = null; | |
| saveToLocalStorage(); | |
| if (notes.length > 0) { | |
| selectNote(notes[0].id); | |
| } else { | |
| window.createNewNote(); | |
| } | |
| } | |
| }; | |
| /** | |
| * Events | |
| */ | |
| bodyInput.addEventListener('input', updateNote); | |
| window.addEventListener('keydown', (e) => { | |
| const isCtrl = e.ctrlKey || e.metaKey; // Support Cmd on Mac too | |
| if (isCtrl && e.key === 'n') { | |
| e.preventDefault(); | |
| window.createNewNote(); | |
| } else if (isCtrl && e.key === 'j') { | |
| e.preventDefault(); | |
| navigateNotes('prev'); | |
| } else if (isCtrl && e.key === 'k') { | |
| e.preventDefault(); | |
| navigateNotes('next'); | |
| } else if (isCtrl && e.key === 'p') { | |
| e.preventDefault(); | |
| window.toggleMode(); | |
| } | |
| }); | |
| /** | |
| * Init | |
| */ | |
| if (notes.length > 0) { | |
| selectNote(notes[0].id); | |
| } else { | |
| window.createNewNote(); | |
| } | |
| })(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment