Skip to content

Instantly share code, notes, and snippets.

@mdigan
Last active February 1, 2026 21:21
Show Gist options
  • Select an option

  • Save mdigan/56f431a55a1cf7a070009427c09e74be to your computer and use it in GitHub Desktop.

Select an option

Save mdigan/56f431a55a1cf7a070009427c09e74be to your computer and use it in GitHub Desktop.
My Personal RSS Reader
<!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>
<!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