notion-lite / index.html
sumitjangir's picture
Add 3 files
24a4f6d verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notion Lite - Professional Note Taking</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.sidebar {
transition: all 0.3s ease;
}
.note-content {
min-height: calc(100vh - 150px);
}
.drag-handle {
cursor: move;
}
.block-menu {
opacity: 0;
transition: opacity 0.2s ease;
}
.note-block:hover .block-menu {
opacity: 1;
}
.prose :where(h1):not(:where([class~="not-prose"] *)) {
font-size: 2em;
margin-top: 0;
margin-bottom: 0.5em;
font-weight: 700;
}
.prose :where(h2):not(:where([class~="not-prose"] *)) {
font-size: 1.5em;
margin-top: 1.5em;
margin-bottom: 0.5em;
font-weight: 600;
}
.prose :where(p):not(:where([class~="not-prose"] *)) {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.prose :where(ul):not(:where([class~="not-prose"] *)) {
list-style-type: disc;
padding-left: 1.5em;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.prose :where(ol):not(:where([class~="not-prose"] *)) {
list-style-type: decimal;
padding-left: 1.5em;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.prose :where(blockquote):not(:where([class~="not-prose"] *)) {
border-left: 4px solid #e5e7eb;
padding-left: 1em;
margin-left: 0;
font-style: italic;
color: #6b7280;
}
.prose :where(code):not(:where([class~="not-prose"] *)) {
background-color: #f3f4f6;
padding: 0.2em 0.4em;
border-radius: 0.25em;
font-family: monospace;
}
.prose :where(pre):not(:where([class~="not-prose"] *)) {
background-color: #1e293b;
color: white;
padding: 1em;
border-radius: 0.5em;
overflow-x: auto;
}
</style>
</head>
<body class="bg-gray-50 text-gray-800 flex h-screen overflow-hidden">
<!-- Sidebar -->
<div class="sidebar bg-white w-64 border-r border-gray-200 flex flex-col h-full">
<div class="p-4 border-b border-gray-200 flex items-center justify-between">
<h1 class="text-xl font-bold text-gray-800 flex items-center">
<i class="fas fa-book-open mr-2 text-blue-500"></i>
Notion Lite
</h1>
<button id="sidebar-toggle" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-chevron-left"></i>
</button>
</div>
<div class="p-4">
<button id="new-note-btn" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded-md flex items-center justify-center">
<i class="fas fa-plus mr-2"></i> New Note
</button>
</div>
<div class="px-2">
<div class="flex items-center px-2 py-1 text-gray-500 text-sm font-medium">
<i class="fas fa-thumbtack mr-2 text-xs"></i>
<span>Pinned</span>
</div>
<div id="pinned-notes" class="mt-1">
<!-- Pinned notes will appear here -->
</div>
<div class="flex items-center px-2 py-1 mt-4 text-gray-500 text-sm font-medium">
<i class="fas fa-file-alt mr-2 text-xs"></i>
<span>All Notes</span>
</div>
<div id="all-notes" class="mt-1">
<!-- All notes will appear here -->
</div>
</div>
<div class="mt-auto p-4 border-t border-gray-200">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center text-blue-500">
<i class="fas fa-user"></i>
</div>
<div class="ml-2">
<div class="text-sm font-medium">User</div>
<div class="text-xs text-gray-500">Free Plan</div>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Toolbar -->
<div class="bg-white border-b border-gray-200 p-3 flex items-center justify-between">
<div class="flex items-center">
<button id="sidebar-toggle-mobile" class="mr-4 text-gray-500 md:hidden">
<i class="fas fa-bars"></i>
</button>
<div id="note-title" class="text-xl font-semibold px-2 py-1 rounded hover:bg-gray-100 cursor-text" contenteditable="true">Untitled Note</div>
</div>
<div class="flex items-center space-x-2">
<button class="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded">
<i class="fas fa-search"></i>
</button>
<button class="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded">
<i class="fas fa-share-alt"></i>
</button>
<button class="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded">
<i class="fas fa-ellipsis-h"></i>
</button>
</div>
</div>
<!-- Formatting Toolbar -->
<div class="bg-white border-b border-gray-200 p-2 flex items-center overflow-x-auto">
<div class="flex space-x-1">
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="bold">
<i class="fas fa-bold"></i>
</button>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="italic">
<i class="fas fa-italic"></i>
</button>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="underline">
<i class="fas fa-underline"></i>
</button>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="strikethrough">
<i class="fas fa-strikethrough"></i>
</button>
<div class="border-l border-gray-300 mx-1 h-6"></div>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="heading1">
<span class="font-bold">H1</span>
</button>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="heading2">
<span class="font-bold">H2</span>
</button>
<div class="border-l border-gray-300 mx-1 h-6"></div>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="bulletedList">
<i class="fas fa-list-ul"></i>
</button>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="numberedList">
<i class="fas fa-list-ol"></i>
</button>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="checklist">
<i class="fas fa-tasks"></i>
</button>
<div class="border-l border-gray-300 mx-1 h-6"></div>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="quote">
<i class="fas fa-quote-right"></i>
</button>
<button class="format-btn p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" data-format="code">
<i class="fas fa-code"></i>
</button>
<div class="border-l border-gray-300 mx-1 h-6"></div>
<button class="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" id="add-image-btn">
<i class="fas fa-image"></i>
</button>
<button class="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded" id="add-table-btn">
<i class="fas fa-table"></i>
</button>
</div>
</div>
<!-- Note Content -->
<div id="note-editor" class="flex-1 overflow-y-auto p-6 prose max-w-4xl mx-auto note-content">
<div class="note-block relative" data-block-type="paragraph">
<div class="block-menu absolute -left-8 top-0 text-gray-400 flex flex-col">
<button class="drag-handle p-1 hover:text-gray-600 hover:bg-gray-100 rounded">
<i class="fas fa-grip-vertical"></i>
</button>
<button class="delete-block p-1 hover:text-red-500 hover:bg-gray-100 rounded">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="block-content" contenteditable="true" data-placeholder="Type '/' for commands or start writing..."></div>
</div>
</div>
<!-- Status Bar -->
<div class="bg-white border-t border-gray-200 p-2 text-xs text-gray-500 flex justify-between items-center">
<div>
<span id="word-count">0 words</span>
<span class="mx-2"></span>
<span id="char-count">0 characters</span>
</div>
<div>
<button id="toggle-dark-mode" class="p-1 hover:bg-gray-100 rounded">
<i class="fas fa-moon"></i>
</button>
</div>
</div>
</div>
<!-- Block Menu (hidden by default) -->
<div id="block-menu" class="hidden absolute z-10 w-56 bg-white rounded-md shadow-lg border border-gray-200">
<div class="p-1">
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="heading1">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<span class="font-bold text-sm">H1</span>
</div>
<span>Heading 1</span>
</div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="heading2">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<span class="font-bold text-sm">H2</span>
</div>
<span>Heading 2</span>
</div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="paragraph">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-paragraph text-xs"></i>
</div>
<span>Text</span>
</div>
<div class="border-t border-gray-200 my-1"></div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="bulletedList">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-list-ul text-xs"></i>
</div>
<span>Bulleted List</span>
</div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="numberedList">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-list-ol text-xs"></i>
</div>
<span>Numbered List</span>
</div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="checklist">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-tasks text-xs"></i>
</div>
<span>Checklist</span>
</div>
<div class="border-t border-gray-200 my-1"></div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="quote">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-quote-right text-xs"></i>
</div>
<span>Quote</span>
</div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="code">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-code text-xs"></i>
</div>
<span>Code Block</span>
</div>
<div class="border-t border-gray-200 my-1"></div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="image">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-image text-xs"></i>
</div>
<span>Image</span>
</div>
<div class="block-option px-3 py-2 text-sm hover:bg-gray-100 rounded cursor-pointer flex items-center" data-block-type="table">
<div class="w-6 h-6 rounded bg-gray-100 flex items-center justify-center mr-2">
<i class="fas fa-table text-xs"></i>
</div>
<span>Table</span>
</div>
</div>
</div>
<script>
// State management
let notes = [];
let currentNoteId = null;
let isDarkMode = false;
// DOM elements
const sidebar = document.querySelector('.sidebar');
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebarToggleMobile = document.getElementById('sidebar-toggle-mobile');
const newNoteBtn = document.getElementById('new-note-btn');
const noteEditor = document.getElementById('note-editor');
const noteTitle = document.getElementById('note-title');
const pinnedNotesContainer = document.getElementById('pinned-notes');
const allNotesContainer = document.getElementById('all-notes');
const blockMenu = document.getElementById('block-menu');
const formatButtons = document.querySelectorAll('.format-btn');
const wordCountEl = document.getElementById('word-count');
const charCountEl = document.getElementById('char-count');
const toggleDarkModeBtn = document.getElementById('toggle-dark-mode');
const addImageBtn = document.getElementById('add-image-btn');
const addTableBtn = document.getElementById('add-table-btn');
// Initialize the app
document.addEventListener('DOMContentLoaded', () => {
loadNotes();
setupEventListeners();
createNewNote();
updateCounts();
});
function setupEventListeners() {
// Sidebar toggle
sidebarToggle.addEventListener('click', toggleSidebar);
sidebarToggleMobile.addEventListener('click', toggleSidebar);
// New note button
newNoteBtn.addEventListener('click', createNewNote);
// Note title editing
noteTitle.addEventListener('blur', saveCurrentNote);
noteTitle.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
noteTitle.blur();
}
});
// Formatting buttons
formatButtons.forEach(btn => {
btn.addEventListener('click', () => {
const format = btn.dataset.format;
applyFormat(format);
});
});
// Block menu
document.addEventListener('click', (e) => {
if (e.target.closest('.block-content') && e.target.closest('.block-content').textContent === '') {
const blockContent = e.target.closest('.block-content');
if (blockContent.textContent === '' || blockContent.textContent === '/') {
showBlockMenu(blockContent);
}
}
});
// Block options
document.querySelectorAll('.block-option').forEach(option => {
option.addEventListener('click', (e) => {
const blockType = option.dataset.blockType;
const blockContent = blockMenu.dataset.targetBlock;
if (blockContent) {
changeBlockType(blockContent, blockType);
}
hideBlockMenu();
});
});
// Add image button
addImageBtn.addEventListener('click', () => {
addBlock('image');
});
// Add table button
addTableBtn.addEventListener('click', () => {
addBlock('table');
});
// Dark mode toggle
toggleDarkModeBtn.addEventListener('click', toggleDarkMode);
// Click outside block menu to hide it
document.addEventListener('click', (e) => {
if (!e.target.closest('#block-menu') && !e.target.closest('.block-content')) {
hideBlockMenu();
}
});
// Handle slash commands
noteEditor.addEventListener('keydown', (e) => {
const blockContent = e.target.closest('.block-content');
if (e.key === '/' && blockContent && blockContent.textContent === '') {
e.preventDefault();
showBlockMenu(blockContent);
} else if (e.key === 'Enter' && blockContent) {
handleEnterKey(blockContent, e);
} else if (e.key === 'Backspace' && blockContent) {
handleBackspaceKey(blockContent, e);
}
});
// Update counts on input
noteEditor.addEventListener('input', () => {
updateCounts();
saveCurrentNote();
});
}
function toggleSidebar() {
sidebar.classList.toggle('-ml-64');
sidebar.classList.toggle('md:-ml-64');
sidebarToggle.querySelector('i').classList.toggle('fa-chevron-left');
sidebarToggle.querySelector('i').classList.toggle('fa-chevron-right');
}
function createNewNote() {
const newNote = {
id: Date.now().toString(),
title: 'Untitled Note',
content: '',
createdAt: new Date(),
updatedAt: new Date(),
pinned: false
};
notes.push(newNote);
currentNoteId = newNote.id;
saveNotes();
renderNotesList();
renderNote(newNote);
}
function loadNotes() {
const savedNotes = localStorage.getItem('notion-lite-notes');
if (savedNotes) {
notes = JSON.parse(savedNotes);
if (notes.length > 0) {
renderNotesList();
currentNoteId = notes[0].id;
renderNote(notes[0]);
}
}
}
function saveNotes() {
localStorage.setItem('notion-lite-notes', JSON.stringify(notes));
}
function saveCurrentNote() {
if (!currentNoteId) return;
const noteIndex = notes.findIndex(note => note.id === currentNoteId);
if (noteIndex === -1) return;
// Update title
notes[noteIndex].title = noteTitle.textContent || 'Untitled Note';
// Update content
const blocks = Array.from(document.querySelectorAll('.note-block')).map(blockEl => {
return {
type: blockEl.dataset.blockType,
content: blockEl.querySelector('.block-content').innerHTML
};
});
notes[noteIndex].content = JSON.stringify(blocks);
notes[noteIndex].updatedAt = new Date();
saveNotes();
renderNotesList();
}
function renderNotesList() {
// Clear existing notes
pinnedNotesContainer.innerHTML = '';
allNotesContainer.innerHTML = '';
// Separate pinned and unpinned notes
const pinned = notes.filter(note => note.pinned);
const unpinned = notes.filter(note => !note.pinned);
// Sort by updated date (newest first)
pinned.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
unpinned.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
// Render pinned notes
pinned.forEach(note => {
pinnedNotesContainer.appendChild(createNoteListItem(note));
});
// Render unpinned notes
unpinned.forEach(note => {
allNotesContainer.appendChild(createNoteListItem(note));
});
}
function createNoteListItem(note) {
const noteEl = document.createElement('div');
noteEl.className = `note-item flex items-center px-2 py-2 rounded cursor-pointer ${note.id === currentNoteId ? 'bg-blue-50 text-blue-600' : 'hover:bg-gray-100'}`;
noteEl.innerHTML = `
<div class="flex-1 truncate">
<div class="font-medium truncate">${note.title}</div>
<div class="text-xs text-gray-500 truncate">${formatDate(note.updatedAt)}</div>
</div>
<button class="note-pin-btn p-1 text-gray-400 hover:text-yellow-500 rounded-full ml-2">
<i class="fas fa-thumbtack ${note.pinned ? 'text-yellow-500' : ''}"></i>
</button>
`;
noteEl.addEventListener('click', () => {
currentNoteId = note.id;
renderNote(note);
renderNotesList();
});
const pinBtn = noteEl.querySelector('.note-pin-btn');
pinBtn.addEventListener('click', (e) => {
e.stopPropagation();
note.pinned = !note.pinned;
saveNotes();
renderNotesList();
});
return noteEl;
}
function renderNote(note) {
// Update title
noteTitle.textContent = note.title;
// Clear editor
noteEditor.innerHTML = '';
// Parse and render content
let blocks = [];
try {
blocks = JSON.parse(note.content || '[]');
} catch (e) {
blocks = [];
}
if (blocks.length === 0) {
// Default empty block
addBlock('paragraph');
} else {
blocks.forEach(block => {
addBlock(block.type, block.content);
});
}
// Focus the first block
const firstBlock = noteEditor.querySelector('.block-content');
if (firstBlock) {
firstBlock.focus();
}
updateCounts();
}
function addBlock(type, content = '') {
const blockEl = document.createElement('div');
blockEl.className = 'note-block relative my-2 group';
blockEl.dataset.blockType = type;
let blockContent = '';
let placeholder = 'Type \'/\' for commands or start writing...';
switch (type) {
case 'heading1':
blockContent = `<h1>${content || ''}</h1>`;
placeholder = 'Heading 1';
break;
case 'heading2':
blockContent = `<h2>${content || ''}</h2>`;
placeholder = 'Heading 2';
break;
case 'bulletedList':
blockContent = `<ul><li>${content || ''}</li></ul>`;
placeholder = 'List item';
break;
case 'numberedList':
blockContent = `<ol><li>${content || ''}</li></ol>`;
placeholder = 'List item';
break;
case 'checklist':
blockContent = `<div class="flex items-center"><input type="checkbox" class="mr-2"><span>${content || ''}</span></div>`;
placeholder = 'Todo item';
break;
case 'quote':
blockContent = `<blockquote>${content || ''}</blockquote>`;
placeholder = 'Quote';
break;
case 'code':
blockContent = `<pre><code>${content || ''}</code></pre>`;
placeholder = 'Code';
break;
case 'image':
blockContent = `<div class="image-block">
<div class="flex items-center justify-center border-2 border-dashed border-gray-300 rounded p-4">
<button class="bg-blue-500 hover:bg-blue-600 text-white py-1 px-3 rounded text-sm">
<i class="fas fa-upload mr-1"></i> Upload Image
</button>
</div>
</div>`;
break;
case 'table':
blockContent = `<div class="table-block">
<table class="border-collapse w-full">
<thead>
<tr>
<th class="border border-gray-300 p-2 bg-gray-100" contenteditable="true">Header 1</th>
<th class="border border-gray-300 p-2 bg-gray-100" contenteditable="true">Header 2</th>
</tr>
</thead>
<tbody>
<tr>
<td class="border border-gray-300 p-2" contenteditable="true">Cell 1</td>
<td class="border border-gray-300 p-2" contenteditable="true">Cell 2</td>
</tr>
</tbody>
</table>
<div class="mt-2 flex">
<button class="text-xs bg-gray-100 hover:bg-gray-200 px-2 py-1 rounded mr-1">
<i class="fas fa-plus mr-1"></i> Row
</button>
<button class="text-xs bg-gray-100 hover:bg-gray-200 px-2 py-1 rounded mr-1">
<i class="fas fa-plus mr-1"></i> Column
</button>
<button class="text-xs bg-gray-100 hover:bg-gray-200 px-2 py-1 rounded">
<i class="fas fa-trash mr-1"></i> Delete
</button>
</div>
</div>`;
break;
default:
blockContent = `<p>${content || ''}</p>`;
}
blockEl.innerHTML = `
<div class="block-menu absolute -left-8 top-0 text-gray-400 flex flex-col opacity-0 group-hover:opacity-100 transition-opacity">
<button class="drag-handle p-1 hover:text-gray-600 hover:bg-gray-100 rounded">
<i class="fas fa-grip-vertical"></i>
</button>
<button class="delete-block p-1 hover:text-red-500 hover:bg-gray-100 rounded">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="block-content" contenteditable="true" data-placeholder="${placeholder}">
${blockContent}
</div>
`;
noteEditor.appendChild(blockEl);
// Set up event listeners for the new block
const blockContentEl = blockEl.querySelector('.block-content');
const deleteBtn = blockEl.querySelector('.delete-block');
deleteBtn.addEventListener('click', () => {
blockEl.remove();
saveCurrentNote();
});
blockContentEl.addEventListener('focus', () => {
blockEl.querySelector('.block-menu').classList.remove('opacity-0');
});
blockContentEl.addEventListener('blur', () => {
if (blockContentEl.innerHTML === '' || blockContentEl.innerHTML === '<br>') {
if (type === 'heading1') {
blockContentEl.innerHTML = '<h1><br></h1>';
} else if (type === 'heading2') {
blockContentEl.innerHTML = '<h2><br></h2>';
} else if (type === 'bulletedList') {
blockContentEl.innerHTML = '<ul><li><br></li></ul>';
} else if (type === 'numberedList') {
blockContentEl.innerHTML = '<ol><li><br></li></ol>';
} else if (type === 'quote') {
blockContentEl.innerHTML = '<blockquote><br></blockquote>';
} else if (type === 'code') {
blockContentEl.innerHTML = '<pre><code><br></code></pre>';
} else {
blockContentEl.innerHTML = '<p><br></p>';
}
}
saveCurrentNote();
});
// Focus the new block if it's empty
if (content === '') {
setTimeout(() => {
blockContentEl.focus();
// Move cursor to the end
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(blockContentEl);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
}, 10);
}
return blockEl;
}
function changeBlockType(blockContentEl, newType) {
const blockEl = blockContentEl.closest('.note-block');
const currentContent = blockContentEl.innerHTML;
// Remove all child nodes to get clean text content
const tempDiv = document.createElement('div');
tempDiv.innerHTML = currentContent;
const textContent = tempDiv.textContent || '';
blockEl.dataset.blockType = newType;
switch (newType) {
case 'heading1':
blockContentEl.innerHTML = `<h1>${textContent}</h1>`;
break;
case 'heading2':
blockContentEl.innerHTML = `<h2>${textContent}</h2>`;
break;
case 'bulletedList':
blockContentEl.innerHTML = `<ul><li>${textContent}</li></ul>`;
break;
case 'numberedList':
blockContentEl.innerHTML = `<ol><li>${textContent}</li></ol>`;
break;
case 'checklist':
blockContentEl.innerHTML = `<div class="flex items-center"><input type="checkbox" class="mr-2"><span>${textContent}</span></div>`;
break;
case 'quote':
blockContentEl.innerHTML = `<blockquote>${textContent}</blockquote>`;
break;
case 'code':
blockContentEl.innerHTML = `<pre><code>${textContent}</code></pre>`;
break;
default:
blockContentEl.innerHTML = `<p>${textContent}</p>`;
}
saveCurrentNote();
blockContentEl.focus();
}
function showBlockMenu(blockContentEl) {
hideBlockMenu();
const rect = blockContentEl.getBoundingClientRect();
blockMenu.style.top = `${rect.top + window.scrollY}px`;
blockMenu.style.left = `${rect.left + window.scrollX}px`;
blockMenu.dataset.targetBlock = blockContentEl.id || Date.now().toString();
blockContentEl.id = blockMenu.dataset.targetBlock;
blockMenu.classList.remove('hidden');
}
function hideBlockMenu() {
blockMenu.classList.add('hidden');
delete blockMenu.dataset.targetBlock;
}
function applyFormat(format) {
document.execCommand(format, false, null);
// Special handling for headings and other complex formats
const selection = window.getSelection();
if (!selection.rangeCount) return;
const range = selection.getRangeAt(0);
const blockContentEl = range.startContainer.closest('.block-content');
if (!blockContentEl) return;
switch (format) {
case 'heading1':
changeBlockType(blockContentEl, 'heading1');
break;
case 'heading2':
changeBlockType(blockContentEl, 'heading2');
break;
case 'bulletedList':
changeBlockType(blockContentEl, 'bulletedList');
break;
case 'numberedList':
changeBlockType(blockContentEl, 'numberedList');
break;
case 'checklist':
changeBlockType(blockContentEl, 'checklist');
break;
case 'quote':
changeBlockType(blockContentEl, 'quote');
break;
case 'code':
changeBlockType(blockContentEl, 'code');
break;
}
}
function handleEnterKey(blockContentEl, event) {
const blockEl = blockContentEl.closest('.note-block');
const blockType = blockEl.dataset.blockType;
// For lists, continue the same list type
if (blockType === 'bulletedList' || blockType === 'numberedList') {
// If the list item is empty, break out of the list
if (blockContentEl.textContent.trim() === '') {
event.preventDefault();
changeBlockType(blockContentEl, 'paragraph');
} else {
// Otherwise continue the list
event.preventDefault();
const newBlock = addBlock(blockType);
setTimeout(() => {
newBlock.querySelector('.block-content').focus();
}, 10);
}
} else {
// For other blocks, just add a new paragraph
event.preventDefault();
const newBlock = addBlock('paragraph');
setTimeout(() => {
newBlock.querySelector('.block-content').focus();
}, 10);
}
}
function handleBackspaceKey(blockContentEl, event) {
// Only handle if the block is empty
if (blockContentEl.textContent.trim() !== '') return;
const blockEl = blockContentEl.closest('.note-block');
const blockType = blockEl.dataset.blockType;
// For headings, convert to paragraph
if (blockType === 'heading1' || blockType === 'heading2') {
event.preventDefault();
changeBlockType(blockContentEl, 'paragraph');
}
// For lists, convert to paragraph
if (blockType === 'bulletedList' || blockType === 'numberedList' || blockType === 'checklist') {
event.preventDefault();
changeBlockType(blockContentEl, 'paragraph');
}
// For quotes and code blocks, convert to paragraph
if (blockType === 'quote' || blockType === 'code') {
event.preventDefault();
changeBlockType(blockContentEl, 'paragraph');
}
}
function updateCounts() {
const text = noteEditor.textContent;
const wordCount = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
const charCount = text.length;
wordCountEl.textContent = `${wordCount} ${wordCount === 1 ? 'word' : 'words'}`;
charCountEl.textContent = `${charCount} ${charCount === 1 ? 'character' : 'characters'}`;
}
function toggleDarkMode() {
isDarkMode = !isDarkMode;
if (isDarkMode) {
document.body.classList.add('bg-gray-900', 'text-gray-100');
document.body.classList.remove('bg-gray-50', 'text-gray-800');
toggleDarkModeBtn.innerHTML = '<i class="fas fa-sun"></i>';
// Update sidebar
sidebar.classList.add('bg-gray-800', 'border-gray-700');
sidebar.classList.remove('bg-white', 'border-gray-200');
// Update editor
noteEditor.classList.add('bg-gray-900');
noteEditor.classList.remove('bg-gray-50');
} else {
document.body.classList.remove('bg-gray-900', 'text-gray-100');
document.body.classList.add('bg-gray-50', 'text-gray-800');
toggleDarkModeBtn.innerHTML = '<i class="fas fa-moon"></i>';
// Update sidebar
sidebar.classList.remove('bg-gray-800', 'border-gray-700');
sidebar.classList.add('bg-white', 'border-gray-200');
// Update editor
noteEditor.classList.remove('bg-gray-900');
noteEditor.classList.add('bg-gray-50');
}
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: date.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined
});
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sumitjangir/notion-lite" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>