Harry00 commited on
Commit
dcbce5a
·
verified ·
1 Parent(s): edb4184

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +956 -626
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Notion Lite</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <script>
@@ -39,8 +39,8 @@
39
  '100%': { opacity: '1' },
40
  },
41
  slideIn: {
42
- '0%': { transform: 'translateX(20px)', opacity: '0' },
43
- '100%': { transform: 'translateX(0)', opacity: '1' },
44
  }
45
  }
46
  }
@@ -48,750 +48,1080 @@
48
  }
49
  </script>
50
  <style>
51
- .ProseMirror {
52
- min-height: 100px;
53
- padding: 10px;
54
- outline: none;
55
- }
56
- .ProseMirror:focus {
57
- outline: none;
58
- }
59
- .ProseMirror p {
60
- margin-bottom: 1rem;
61
  }
62
- .ProseMirror h1 {
63
  font-size: 2rem;
64
- font-weight: bold;
65
- margin: 1rem 0;
 
 
66
  }
67
- .ProseMirror h2 {
68
  font-size: 1.5rem;
69
- font-weight: bold;
70
- margin: 0.75rem 0;
 
 
71
  }
72
- .ProseMirror h3 {
73
  font-size: 1.25rem;
74
- font-weight: bold;
75
- margin: 0.5rem 0;
 
 
76
  }
77
- .ProseMirror ul, .ProseMirror ol {
78
- padding-left: 1.5rem;
79
  margin-bottom: 1rem;
80
  }
81
- .ProseMirror ul {
82
  list-style-type: disc;
 
 
 
83
  }
84
- .ProseMirror ol {
85
  list-style-type: decimal;
 
 
 
86
  }
87
- .ProseMirror blockquote {
88
- border-left: 3px solid #94a3b8;
89
  padding-left: 1rem;
90
- margin: 1rem 0;
91
- color: #64748b;
92
- }
93
- .ProseMirror pre {
94
- background: #1e293b;
95
- color: #f8fafc;
96
- padding: 1rem;
97
- border-radius: 0.5rem;
98
- margin: 1rem 0;
99
- overflow-x: auto;
100
  }
101
- .ProseMirror code {
102
- background: #e2e8f0;
103
- padding: 0.2rem 0.4rem;
104
  border-radius: 0.25rem;
 
105
  font-family: monospace;
106
  }
107
- .ProseMirror table {
108
- border-collapse: collapse;
109
- margin: 1rem 0;
110
- width: 100%;
111
- }
112
- .ProseMirror th, .ProseMirror td {
113
- border: 1px solid #cbd5e1;
114
- padding: 0.5rem;
115
- }
116
- .ProseMirror th {
117
- background-color: #f1f5f9;
118
- }
119
- .ProseMirror img {
120
- max-width: 100%;
121
- height: auto;
122
- margin: 1rem 0;
123
  border-radius: 0.5rem;
 
 
 
 
124
  }
125
- .ProseMirror a {
126
  color: #3b82f6;
127
  text-decoration: underline;
128
  }
129
- .ProseMirror .task-item {
130
- display: flex;
131
- align-items: center;
132
- margin-bottom: 0.5rem;
133
  }
134
- .ProseMirror .task-item input[type="checkbox"] {
135
- margin-right: 0.5rem;
136
  }
137
- .ProseMirror .task-item.checked {
138
- color: #64748b;
139
- text-decoration: line-through;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  }
141
- .custom-scrollbar::-webkit-scrollbar {
142
- width: 6px;
143
- height: 6px;
144
  }
145
- .custom-scrollbar::-webkit-scrollbar-track {
146
- background: #f1f5f9;
 
147
  }
148
- .custom-scrollbar::-webkit-scrollbar-thumb {
149
- background: #cbd5e1;
150
- border-radius: 3px;
 
151
  }
152
- .dark .custom-scrollbar::-webkit-scrollbar-track {
153
- background: #1e293b;
154
  }
155
- .dark .custom-scrollbar::-webkit-scrollbar-thumb {
156
- background: #475569;
 
 
 
157
  }
158
- .resize-handle {
159
- position: absolute;
160
- right: 0;
161
- top: 0;
162
- bottom: 0;
163
- width: 4px;
164
- background: #cbd5e1;
165
- cursor: col-resize;
166
- transition: background 0.2s;
167
  }
168
- .resize-handle:hover {
169
- background: #94a3b8;
 
170
  }
171
- .dark .resize-handle {
172
- background: #475569;
 
173
  }
174
- .dark .resize-handle:hover {
175
- background: #64748b;
 
 
 
176
  }
177
- .floating-toolbar {
 
 
 
 
 
178
  opacity: 0;
179
- transform: translateY(10px);
180
- transition: opacity 0.2s, transform 0.2s;
181
  }
182
- .floating-toolbar.visible {
183
  opacity: 1;
184
- transform: translateY(0);
185
  }
186
- .slate-insert-menu {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  opacity: 0;
188
- transform: translateY(10px);
189
- transition: opacity 0.2s, transform 0.2s;
190
- pointer-events: none;
191
  }
192
- .slate-insert-menu.visible {
 
193
  opacity: 1;
194
- transform: translateY(0);
195
- pointer-events: all;
 
 
196
  }
197
  </style>
198
  </head>
199
- <body class="bg-gray-50 dark:bg-dark-900 text-gray-800 dark:text-gray-200 transition-colors duration-200 min-h-screen flex">
200
- <!-- Sidebar -->
201
- <div class="w-64 bg-white dark:bg-dark-800 border-r border-gray-200 dark:border-gray-700 flex flex-col h-screen sticky top-0">
202
- <div class="p-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
203
- <div class="flex items-center space-x-2">
204
- <div class="w-8 h-8 rounded-md bg-primary-500 flex items-center justify-center text-white">
205
- <i class="fas fa-book"></i>
 
 
 
206
  </div>
207
- <span class="font-semibold">Notion Lite</span>
208
- </div>
209
- <button id="toggle-dark" class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700">
210
- <i class="fas fa-moon dark:hidden"></i>
211
- <i class="fas fa-sun hidden dark:block"></i>
212
- </button>
213
- </div>
214
- <div class="flex-1 overflow-y-auto custom-scrollbar p-2">
215
- <div class="space-y-1">
216
- <button id="new-page" class="w-full flex items-center space-x-2 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
217
- <i class="fas fa-plus text-gray-500 dark:text-gray-400"></i>
218
- <span>New page</span>
219
  </button>
220
- <div class="px-2 py-1 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Pages</div>
221
- <div id="pages-list" class="space-y-1">
222
- <!-- Pages will be loaded here -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  </div>
224
  </div>
225
  </div>
226
- <div class="p-4 border-t border-gray-200 dark:border-gray-700">
227
- <div class="flex items-center space-x-2">
228
- <div class="w-8 h-8 rounded-full bg-primary-500 flex items-center justify-center text-white">
229
- <span class="text-sm">U</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  </div>
231
- <span class="text-sm font-medium">User</span>
232
  </div>
233
  </div>
234
  </div>
235
 
236
- <!-- Main Content -->
237
- <div class="flex-1 flex flex-col h-screen overflow-hidden">
238
- <!-- Toolbar -->
239
- <div class="bg-white dark:bg-dark-800 border-b border-gray-200 dark:border-gray-700 p-2 flex items-center justify-between">
240
- <div class="flex items-center space-x-2">
241
- <button id="toggle-sidebar" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 md:hidden">
242
- <i class="fas fa-bars"></i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  </button>
244
- <div id="breadcrumbs" class="flex items-center space-x-1 text-sm">
245
- <!-- Breadcrumbs will be loaded here -->
246
- </div>
247
  </div>
248
- <div class="flex items-center space-x-2">
249
- <button id="save-button" class="px-3 py-1 rounded-lg bg-primary-500 text-white hover:bg-primary-600 flex items-center space-x-1">
250
- <i class="fas fa-save"></i>
251
- <span class="hidden md:inline">Save</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  </button>
253
  </div>
254
- </div>
255
-
256
- <!-- Editor Area -->
257
- <div class="flex-1 overflow-auto custom-scrollbar">
258
- <div id="editor-container" class="max-w-4xl mx-auto p-4">
259
- <div id="editor" class="ProseMirror"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  </div>
261
  </div>
262
  </div>
263
 
264
- <!-- Floating Format Toolbar -->
265
- <div id="floating-toolbar" class="floating-toolbar fixed bg-white dark:bg-dark-800 shadow-lg rounded-lg p-1 border border-gray-200 dark:border-gray-700 z-50 flex items-center space-x-1">
266
- <button data-command="bold" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
267
  <i class="fas fa-bold"></i>
268
  </button>
269
- <button data-command="italic" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
270
  <i class="fas fa-italic"></i>
271
  </button>
272
- <button data-command="underline" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
273
  <i class="fas fa-underline"></i>
274
  </button>
275
- <button data-command="strike" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
276
  <i class="fas fa-strikethrough"></i>
277
  </button>
278
  <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>
279
- <button data-command="heading1" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
280
- <span class="font-bold">H1</span>
281
  </button>
282
- <button data-command="heading2" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
283
- <span class="font-bold">H2</span>
284
  </button>
285
- <button data-command="heading3" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
286
- <span class="font-bold">H3</span>
287
  </button>
288
  <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>
289
- <button data-command="bulletList" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
290
  <i class="fas fa-list-ul"></i>
291
  </button>
292
- <button data-command="orderedList" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
293
  <i class="fas fa-list-ol"></i>
294
  </button>
295
- <button data-command="taskList" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
296
- <i class="fas fa-tasks"></i>
297
- </button>
298
  <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>
299
- <button data-command="blockquote" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
300
- <i class="fas fa-quote-right"></i>
301
  </button>
302
- <button data-command="codeBlock" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
303
- <i class="fas fa-code"></i>
 
 
 
 
304
  </button>
305
- <button data-command="horizontalRule" class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700">
306
- <i class="fas fa-minus"></i>
307
  </button>
308
  </div>
309
 
310
- <!-- Slate Insert Menu -->
311
- <div id="slate-insert-menu" class="slate-insert-menu fixed bg-white dark:bg-dark-800 shadow-lg rounded-lg p-2 border border-gray-200 dark:border-gray-700 z-50 w-64">
312
- <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 px-2 py-1">Insert</div>
313
- <div class="space-y-1">
314
- <button data-insert="heading1" class="w-full flex items-center space-x-2 p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
315
- <div class="w-8 h-8 rounded bg-blue-100 dark:bg-blue-900 flex items-center justify-center">
316
- <span class="font-bold text-blue-600 dark:text-blue-300">H1</span>
317
- </div>
318
- <div>
319
- <div class="font-medium">Heading 1</div>
320
- <div class="text-xs text-gray-500 dark:text-gray-400">Large section heading</div>
321
- </div>
322
- </button>
323
- <button data-insert="heading2" class="w-full flex items-center space-x-2 p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
324
- <div class="w-8 h-8 rounded bg-blue-100 dark:bg-blue-900 flex items-center justify-center">
325
- <span class="font-bold text-blue-600 dark:text-blue-300">H2</span>
326
- </div>
327
- <div>
328
- <div class="font-medium">Heading 2</div>
329
- <div class="text-xs text-gray-500 dark:text-gray-400">Medium section heading</div>
330
- </div>
331
- </button>
332
- <button data-insert="bulletList" class="w-full flex items-center space-x-2 p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
333
- <div class="w-8 h-8 rounded bg-purple-100 dark:bg-purple-900 flex items-center justify-center">
334
- <i class="fas fa-list-ul text-purple-600 dark:text-purple-300"></i>
335
- </div>
336
- <div>
337
- <div class="font-medium">Bulleted list</div>
338
- <div class="text-xs text-gray-500 dark:text-gray-400">Create a simple bulleted list</div>
339
- </div>
340
- </button>
341
- <button data-insert="orderedList" class="w-full flex items-center space-x-2 p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
342
- <div class="w-8 h-8 rounded bg-green-100 dark:bg-green-900 flex items-center justify-center">
343
- <i class="fas fa-list-ol text-green-600 dark:text-green-300"></i>
344
- </div>
345
- <div>
346
- <div class="font-medium">Numbered list</div>
347
- <div class="text-xs text-gray-500 dark:text-gray-400">Create a list with numbering</div>
348
- </div>
349
- </button>
350
- <button data-insert="taskList" class="w-full flex items-center space-x-2 p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
351
- <div class="w-8 h-8 rounded bg-yellow-100 dark:bg-yellow-900 flex items-center justify-center">
352
- <i class="fas fa-tasks text-yellow-600 dark:text-yellow-300"></i>
353
- </div>
354
- <div>
355
- <div class="font-medium">Task list</div>
356
- <div class="text-xs text-gray-500 dark:text-gray-400">Track tasks with checkboxes</div>
357
- </div>
358
- </button>
359
- <button data-insert="table" class="w-full flex items-center space-x-2 p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
360
- <div class="w-8 h-8 rounded bg-red-100 dark:bg-red-900 flex items-center justify-center">
361
- <i class="fas fa-table text-red-600 dark:text-red-300"></i>
362
- </div>
363
- <div>
364
- <div class="font-medium">Table</div>
365
- <div class="text-xs text-gray-500 dark:text-gray-400">Insert a table</div>
366
- </div>
367
- </button>
368
- <button data-insert="codeBlock" class="w-full flex items-center space-x-2 p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left">
369
- <div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
370
- <i class="fas fa-code text-gray-600 dark:text-gray-300"></i>
371
- </div>
372
- <div>
373
- <div class="font-medium">Code block</div>
374
- <div class="text-xs text-gray-500 dark:text-gray-400">Insert a block of code</div>
375
  </div>
376
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  </div>
378
  </div>
379
 
380
  <script>
381
- document.addEventListener('DOMContentLoaded', function() {
382
- // Initialize app
383
- class NotionLite {
384
- constructor() {
385
- this.pages = [];
386
- this.currentPage = null;
387
- this.editor = null;
388
- this.initElements();
389
- this.initEventListeners();
390
- this.loadPages();
391
- this.initEditor();
392
- this.updateUI();
393
- }
394
 
395
- initElements() {
396
- this.elements = {
397
- sidebar: document.querySelector('.w-64'),
398
- toggleSidebar: document.getElementById('toggle-sidebar'),
399
- toggleDark: document.getElementById('toggle-dark'),
400
- newPage: document.getElementById('new-page'),
401
- pagesList: document.getElementById('pages-list'),
402
- breadcrumbs: document.getElementById('breadcrumbs'),
403
- saveButton: document.getElementById('save-button'),
404
- editor: document.getElementById('editor'),
405
- editorContainer: document.getElementById('editor-container'),
406
- floatingToolbar: document.getElementById('floating-toolbar'),
407
- slateInsertMenu: document.getElementById('slate-insert-menu'),
408
- };
409
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
 
411
- initEventListeners() {
412
- // Toggle sidebar on mobile
413
- this.elements.toggleSidebar.addEventListener('click', () => {
414
- this.elements.sidebar.classList.toggle('hidden');
415
- });
 
 
 
 
 
 
 
 
416
 
417
- // Toggle dark mode
418
- this.elements.toggleDark.addEventListener('click', () => {
419
- document.documentElement.classList.toggle('dark');
420
- localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
421
- });
 
 
 
422
 
423
- // Create new page
424
- this.elements.newPage.addEventListener('click', () => this.createNewPage());
 
 
425
 
426
- // Save button
427
- this.elements.saveButton.addEventListener('click', () => this.saveCurrentPage());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
 
429
- // Formatting buttons
430
- document.querySelectorAll('[data-command]').forEach(button => {
431
- button.addEventListener('click', (e) => {
432
- const command = e.currentTarget.getAttribute('data-command');
433
- this.executeCommand(command);
434
- });
435
- });
 
 
 
 
 
 
 
436
 
437
- // Insert menu buttons
438
- document.querySelectorAll('[data-insert]').forEach(button => {
439
- button.addEventListener('click', (e) => {
440
- const command = e.currentTarget.getAttribute('data-insert');
441
- this.executeCommand(command);
442
- this.hideInsertMenu();
443
- });
444
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
 
446
- // Editor events
447
- this.elements.editor.addEventListener('click', () => this.updateFloatingToolbar());
448
- this.elements.editor.addEventListener('keyup', () => this.updateFloatingToolbar());
449
- this.elements.editor.addEventListener('input', () => this.saveCurrentPage());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
 
451
- // Slash command
452
- this.elements.editor.addEventListener('keydown', (e) => {
453
- if (e.key === '/' && this.getCurrentTextBeforeCursor() === '') {
454
- this.showInsertMenu();
455
- e.preventDefault();
456
- } else if (e.key === 'Escape') {
457
- this.hideInsertMenu();
458
- }
459
- });
 
 
460
 
461
- // Click outside to hide insert menu
462
- document.addEventListener('click', (e) => {
463
- if (!this.elements.slateInsertMenu.contains(e.target) &&
464
- !this.elements.editor.contains(e.target)) {
465
- this.hideInsertMenu();
466
- }
467
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
 
469
- // Auto-save every 10 seconds
470
- setInterval(() => this.saveCurrentPage(), 10000);
471
- }
 
 
 
 
 
 
 
 
 
472
 
473
- initEditor() {
474
- // Simple content editable with formatting support
475
- this.elements.editor.contentEditable = true;
476
-
477
- // Load first page if exists
478
- if (this.pages.length > 0) {
479
- this.loadPage(this.pages[0].id);
480
- } else {
481
- this.createNewPage();
482
- }
483
- }
 
 
484
 
485
- executeCommand(command) {
486
- document.execCommand('styleWithCSS', false, true);
487
-
488
- switch (command) {
489
- case 'bold':
490
- document.execCommand('bold', false, null);
491
- break;
492
- case 'italic':
493
- document.execCommand('italic', false, null);
494
- break;
495
- case 'underline':
496
- document.execCommand('underline', false, null);
497
- break;
498
- case 'strike':
499
- document.execCommand('strikeThrough', false, null);
500
- break;
501
- case 'heading1':
502
- document.execCommand('formatBlock', false, '<h1>');
503
- break;
504
- case 'heading2':
505
- document.execCommand('formatBlock', false, '<h2>');
506
- break;
507
- case 'heading3':
508
- document.execCommand('formatBlock', false, '<h3>');
509
- break;
510
- case 'bulletList':
511
- document.execCommand('insertUnorderedList', false, null);
512
- break;
513
- case 'orderedList':
514
- document.execCommand('insertOrderedList', false, null);
515
- break;
516
- case 'taskList':
517
- this.insertTaskItem();
518
- break;
519
- case 'blockquote':
520
- document.execCommand('formatBlock', false, '<blockquote>');
521
- break;
522
- case 'codeBlock':
523
- this.insertCodeBlock();
524
- break;
525
- case 'horizontalRule':
526
- document.execCommand('insertHorizontalRule', false, null);
527
- break;
528
- case 'table':
529
- this.insertTable();
530
- break;
531
- }
532
-
533
- this.elements.editor.focus();
534
- this.saveCurrentPage();
535
- }
536
 
537
- insertTaskItem() {
538
- const selection = window.getSelection();
539
- const range = selection.getRangeAt(0);
540
- const container = document.createElement('div');
541
- container.className = 'task-item';
542
-
543
- const checkbox = document.createElement('input');
544
- checkbox.type = 'checkbox';
545
- checkbox.className = 'task-checkbox';
546
- checkbox.addEventListener('change', function() {
547
- if (this.checked) {
548
- container.classList.add('checked');
549
- } else {
550
- container.classList.remove('checked');
551
- }
552
- });
553
-
554
- const text = document.createElement('span');
555
- text.className = 'task-text';
556
- text.contentEditable = true;
557
- text.textContent = 'New task';
558
-
559
- container.appendChild(checkbox);
560
- container.appendChild(text);
561
-
562
- range.deleteContents();
563
- range.insertNode(container);
564
-
565
- // Move cursor inside the task text
566
- const newRange = document.createRange();
567
- newRange.selectNodeContents(text);
568
- newRange.collapse(false);
569
- selection.removeAllRanges();
570
- selection.addRange(newRange);
571
-
572
- text.focus();
573
- }
574
 
575
- insertCodeBlock() {
576
- const selection = window.getSelection();
577
- const range = selection.getRangeAt(0);
578
- const pre = document.createElement('pre');
579
- const code = document.createElement('code');
580
- code.textContent = '// Your code here';
581
- pre.appendChild(code);
582
-
583
- range.deleteContents();
584
- range.insertNode(pre);
585
-
586
- // Move cursor inside the code block
587
- const newRange = document.createRange();
588
- newRange.selectNodeContents(code);
589
- newRange.collapse(false);
590
- selection.removeAllRanges();
591
- selection.addRange(newRange);
592
-
593
- code.focus();
594
- }
595
 
596
- insertTable() {
597
- const selection = window.getSelection();
598
- const range = selection.getRangeAt(0);
599
-
600
- const table = document.createElement('table');
601
- const thead = document.createElement('thead');
602
- const tbody = document.createElement('tbody');
603
- const headerRow = document.createElement('tr');
604
-
605
- // Create header row with 3 columns
606
- for (let i = 0; i < 3; i++) {
607
- const th = document.createElement('th');
608
- th.textContent = i === 0 ? 'Header 1' : i === 1 ? 'Header 2' : 'Header 3';
609
- headerRow.appendChild(th);
610
- }
611
-
612
- // Create 3 rows with 3 columns each
613
- for (let i = 0; i < 3; i++) {
614
- const row = document.createElement('tr');
615
- for (let j = 0; j < 3; j++) {
616
- const td = document.createElement('td');
617
- td.textContent = `Row ${i+1}, Col ${j+1}`;
618
- row.appendChild(td);
619
- }
620
- tbody.appendChild(row);
621
- }
622
-
623
- thead.appendChild(headerRow);
624
- table.appendChild(thead);
625
- table.appendChild(tbody);
626
-
627
- range.deleteContents();
628
- range.insertNode(table);
629
-
630
- // Move cursor after the table
631
- const newRange = document.createRange();
632
- newRange.setStartAfter(table);
633
- newRange.collapse(true);
634
- selection.removeAllRanges();
635
- selection.addRange(newRange);
636
-
637
- this.elements.editor.focus();
638
- }
639
 
640
- updateFloatingToolbar() {
641
- const selection = window.getSelection();
642
- if (!selection.rangeCount) return;
643
-
644
- const range = selection.getRangeAt(0);
645
- const rect = range.getBoundingClientRect();
646
-
647
- // Position the toolbar above the selection
648
- this.elements.floatingToolbar.style.top = `${rect.top + window.scrollY - 40}px`;
649
- this.elements.floatingToolbar.style.left = `${rect.left + window.scrollX - 10}px`;
650
-
651
- // Show the toolbar if there's a selection
652
- if (!selection.isCollapsed) {
653
- this.elements.floatingToolbar.classList.add('visible');
654
- } else {
655
- this.elements.floatingToolbar.classList.remove('visible');
656
- }
657
- }
658
 
659
- showInsertMenu() {
660
- const selection = window.getSelection();
661
- if (!selection.rangeCount) return;
662
-
663
- const range = selection.getRangeAt(0);
664
- const rect = range.getBoundingClientRect();
665
-
666
- // Position the menu below the cursor
667
- this.elements.slateInsertMenu.style.top = `${rect.bottom + window.scrollY + 5}px`;
668
- this.elements.slateInsertMenu.style.left = `${rect.left + window.scrollX}px`;
669
- this.elements.slateInsertMenu.classList.add('visible');
670
- }
671
 
672
- hideInsertMenu() {
673
- this.elements.slateInsertMenu.classList.remove('visible');
674
- }
 
 
 
675
 
676
- getCurrentTextBeforeCursor() {
677
- const selection = window.getSelection();
678
- if (!selection.rangeCount) return '';
679
-
680
- const range = selection.getRangeAt(0);
681
- const node = range.startContainer;
682
- const offset = range.startOffset;
683
-
684
- if (node.nodeType === Node.TEXT_NODE) {
685
- return node.textContent.substring(0, offset);
686
- }
687
-
688
- return '';
689
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
690
 
691
- createNewPage() {
692
- const newPage = {
693
- id: Date.now().toString(),
694
- title: 'Untitled',
695
- content: '',
696
- createdAt: new Date().toISOString(),
697
- updatedAt: new Date().toISOString(),
698
- };
699
-
700
- this.pages.push(newPage);
701
- this.currentPage = newPage;
702
- this.savePages();
703
- this.updateUI();
704
-
705
- // Focus the editor
706
- setTimeout(() => {
707
- this.elements.editor.focus();
708
- }, 100);
709
- }
710
 
711
- loadPage(pageId) {
712
- const page = this.pages.find(p => p.id === pageId);
713
- if (!page) return;
714
-
715
- this.currentPage = page;
716
- this.elements.editor.innerHTML = page.content || '';
717
- this.updateUI();
718
-
719
- // Focus the editor
720
- setTimeout(() => {
721
- this.elements.editor.focus();
722
- }, 100);
 
 
 
 
 
 
723
  }
724
-
725
- saveCurrentPage() {
726
- if (!this.currentPage) return;
727
-
728
- this.currentPage.content = this.elements.editor.innerHTML;
729
- this.currentPage.updatedAt = new Date().toISOString();
730
-
731
- // Extract title from content (first heading or first line of text)
732
- const tempDiv = document.createElement('div');
733
- tempDiv.innerHTML = this.currentPage.content;
734
-
735
- const heading = tempDiv.querySelector('h1, h2, h3');
736
- if (heading) {
737
- this.currentPage.title = heading.textContent.trim();
738
- } else {
739
- const firstText = tempDiv.textContent.trim().split('\n')[0];
740
- this.currentPage.title = firstText || 'Untitled';
741
- }
742
-
743
- this.savePages();
744
- this.updateUI();
745
  }
746
-
747
- loadPages() {
748
- const savedPages = localStorage.getItem('notion-lite-pages');
749
- if (savedPages) {
750
- this.pages = JSON.parse(savedPages);
751
- }
752
-
753
- // Check for dark mode preference
754
- const darkMode = localStorage.getItem('darkMode') === 'true';
755
- if (darkMode) {
756
- document.documentElement.classList.add('dark');
757
- } else {
758
- document.documentElement.classList.remove('dark');
759
- }
760
  }
761
-
762
- savePages() {
763
- localStorage.setItem('notion-lite-pages', JSON.stringify(this.pages));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
  }
765
-
766
- updateUI() {
767
- // Update pages list
768
- this.elements.pagesList.innerHTML = '';
769
-
770
- this.pages.forEach(page => {
771
- const pageElement = document.createElement('button');
772
- pageElement.className = `w-full flex items-center space-x-2 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-left ${this.currentPage?.id === page.id ? 'bg-gray-100 dark:bg-gray-700' : ''}`;
773
- pageElement.innerHTML = `
774
- <i class="fas fa-file-alt text-gray-500 dark:text-gray-400"></i>
775
- <span class="truncate">${page.title}</span>
776
- `;
777
- pageElement.addEventListener('click', () => this.loadPage(page.id));
778
- this.elements.pagesList.appendChild(pageElement);
779
- });
780
-
781
- // Update breadcrumbs
782
- if (this.currentPage) {
783
- this.elements.breadcrumbs.innerHTML = `
784
- <span class="text-gray-500 dark:text-gray-400">Pages</span>
785
- <span class="text-gray-500 dark:text-gray-400">/</span>
786
- <span class="truncate max-w-xs">${this.currentPage.title}</span>
787
- `;
 
 
 
 
 
 
 
 
 
 
 
788
  }
789
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790
  }
 
791
 
792
- // Initialize the app
793
- const app = new NotionLite();
794
- });
795
  </script>
796
  <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=Harry00/notion" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
797
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Notion Lite - Local Note Taking</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <script>
 
39
  '100%': { opacity: '1' },
40
  },
41
  slideIn: {
42
+ '0%': { transform: 'translateY(-10px)', opacity: '0' },
43
+ '100%': { transform: 'translateY(0)', opacity: '1' },
44
  }
45
  }
46
  }
 
48
  }
49
  </script>
50
  <style>
51
+ .prose {
52
+ max-width: 100%;
 
 
 
 
 
 
 
 
53
  }
54
+ .prose :where(h1):not(:where([class~="not-prose"] *)) {
55
  font-size: 2rem;
56
+ margin-top: 0;
57
+ margin-bottom: 1rem;
58
+ font-weight: 700;
59
+ line-height: 1.2;
60
  }
61
+ .prose :where(h2):not(:where([class~="not-prose"] *)) {
62
  font-size: 1.5rem;
63
+ margin-top: 1.5rem;
64
+ margin-bottom: 1rem;
65
+ font-weight: 600;
66
+ line-height: 1.3;
67
  }
68
+ .prose :where(h3):not(:where([class~="not-prose"] *)) {
69
  font-size: 1.25rem;
70
+ margin-top: 1.25rem;
71
+ margin-bottom: 0.75rem;
72
+ font-weight: 600;
73
+ line-height: 1.4;
74
  }
75
+ .prose :where(p):not(:where([class~="not-prose"] *)) {
76
+ margin-top: 1rem;
77
  margin-bottom: 1rem;
78
  }
79
+ .prose :where(ul):not(:where([class~="not-prose"] *)) {
80
  list-style-type: disc;
81
+ padding-left: 1.5rem;
82
+ margin-top: 1rem;
83
+ margin-bottom: 1rem;
84
  }
85
+ .prose :where(ol):not(:where([class~="not-prose"] *)) {
86
  list-style-type: decimal;
87
+ padding-left: 1.5rem;
88
+ margin-top: 1rem;
89
+ margin-bottom: 1rem;
90
  }
91
+ .prose :where(blockquote):not(:where([class~="not-prose"] *)) {
92
+ border-left: 4px solid #e5e7eb;
93
  padding-left: 1rem;
94
+ margin-left: 0;
95
+ margin-top: 1rem;
96
+ margin-bottom: 1rem;
97
+ color: #6b7280;
 
 
 
 
 
 
98
  }
99
+ .prose :where(code):not(:where([class~="not-prose"] *)) {
100
+ background-color: rgba(55, 65, 81, 0.1);
 
101
  border-radius: 0.25rem;
102
+ padding: 0.2rem 0.4rem;
103
  font-family: monospace;
104
  }
105
+ .prose :where(pre):not(:where([class~="not-prose"] *)) {
106
+ background-color: rgba(55, 65, 81, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  border-radius: 0.5rem;
108
+ padding: 1rem;
109
+ overflow-x: auto;
110
+ margin-top: 1rem;
111
+ margin-bottom: 1rem;
112
  }
113
+ .prose :where(a):not(:where([class~="not-prose"] *)) {
114
  color: #3b82f6;
115
  text-decoration: underline;
116
  }
117
+ .dark .prose :where(code):not(:where([class~="not-prose"] *)) {
118
+ background-color: rgba(209, 213, 219, 0.1);
 
 
119
  }
120
+ .dark .prose :where(pre):not(:where([class~="not-prose"] *)) {
121
+ background-color: rgba(209, 213, 219, 0.1);
122
  }
123
+ .dark .prose :where(blockquote):not(:where([class~="not-prose"] *)) {
124
+ border-left-color: #374151;
125
+ color: #9ca3af;
126
+ }
127
+ .editor-content {
128
+ min-height: calc(100vh - 200px);
129
+ }
130
+ .sidebar {
131
+ transition: transform 0.2s ease-in-out;
132
+ }
133
+ @media (max-width: 768px) {
134
+ .sidebar {
135
+ position: fixed;
136
+ top: 0;
137
+ left: 0;
138
+ bottom: 0;
139
+ transform: translateX(-100%);
140
+ z-index: 40;
141
+ }
142
+ .sidebar-open {
143
+ transform: translateX(0);
144
+ }
145
  }
146
+ .table-wrapper {
147
+ overflow-x: auto;
 
148
  }
149
+ table {
150
+ border-collapse: collapse;
151
+ width: 100%;
152
  }
153
+ th, td {
154
+ border: 1px solid #e5e7eb;
155
+ padding: 0.5rem;
156
+ text-align: left;
157
  }
158
+ .dark th, .dark td {
159
+ border-color: #374151;
160
  }
161
+ .to-do-item {
162
+ display: flex;
163
+ align-items: flex-start;
164
+ gap: 0.5rem;
165
+ margin-bottom: 0.5rem;
166
  }
167
+ .to-do-checkbox {
168
+ margin-top: 0.25rem;
169
+ flex-shrink: 0;
170
+ }
171
+ .to-do-content {
172
+ flex-grow: 1;
 
 
 
173
  }
174
+ .to-do-checked {
175
+ opacity: 0.6;
176
+ text-decoration: line-through;
177
  }
178
+ .image-upload-wrapper {
179
+ position: relative;
180
+ margin: 1rem 0;
181
  }
182
+ .image-upload {
183
+ width: 100%;
184
+ max-width: 100%;
185
+ border-radius: 0.5rem;
186
+ display: block;
187
  }
188
+ .image-upload-actions {
189
+ position: absolute;
190
+ top: 0.5rem;
191
+ right: 0.5rem;
192
+ display: flex;
193
+ gap: 0.5rem;
194
  opacity: 0;
195
+ transition: opacity 0.2s;
 
196
  }
197
+ .image-upload-wrapper:hover .image-upload-actions {
198
  opacity: 1;
 
199
  }
200
+ .tooltip {
201
+ position: relative;
202
+ }
203
+ .tooltip-text {
204
+ visibility: hidden;
205
+ width: 120px;
206
+ background-color: #1e293b;
207
+ color: #fff;
208
+ text-align: center;
209
+ border-radius: 6px;
210
+ padding: 5px;
211
+ position: absolute;
212
+ z-index: 1;
213
+ bottom: 125%;
214
+ left: 50%;
215
+ transform: translateX(-50%);
216
  opacity: 0;
217
+ transition: opacity 0.3s;
 
 
218
  }
219
+ .tooltip:hover .tooltip-text {
220
+ visibility: visible;
221
  opacity: 1;
222
+ }
223
+ .dark .tooltip-text {
224
+ background-color: #f8fafc;
225
+ color: #0f172a;
226
  }
227
  </style>
228
  </head>
229
+ <body class="bg-gray-50 dark:bg-dark-900 text-gray-800 dark:text-gray-200 transition-colors duration-200">
230
+ <div class="flex h-screen overflow-hidden">
231
+ <!-- Sidebar -->
232
+ <div class="sidebar w-64 bg-white dark:bg-dark-800 border-r border-gray-200 dark:border-gray-700 flex flex-col">
233
+ <div class="p-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
234
+ <div class="flex items-center gap-2">
235
+ <div class="w-8 h-8 rounded-md bg-primary-500 flex items-center justify-center text-white">
236
+ <i class="fas fa-book"></i>
237
+ </div>
238
+ <h1 class="font-bold text-lg">Notion Lite</h1>
239
  </div>
240
+ <button id="sidebar-close" class="md:hidden p-1 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
241
+ <i class="fas fa-times"></i>
 
 
 
 
 
 
 
 
 
 
242
  </button>
243
+ </div>
244
+ <div class="flex-1 overflow-y-auto p-2">
245
+ <div class="mb-4">
246
+ <div class="flex items-center justify-between px-2 py-1">
247
+ <span class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase">Workspace</span>
248
+ <button id="add-page" class="text-gray-500 dark:text-gray-400 hover:text-primary-500 dark:hover:text-primary-400">
249
+ <i class="fas fa-plus"></i>
250
+ </button>
251
+ </div>
252
+ <div id="page-list" class="mt-1 space-y-1">
253
+ <!-- Pages will be loaded here -->
254
+ </div>
255
+ </div>
256
+ <div class="mb-4">
257
+ <div class="flex items-center justify-between px-2 py-1">
258
+ <span class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase">Quick Links</span>
259
+ </div>
260
+ <div class="mt-1 space-y-1">
261
+ <a href="#" class="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 text-sm">
262
+ <i class="fas fa-inbox text-gray-500 dark:text-gray-400"></i>
263
+ <span>Inbox</span>
264
+ </a>
265
+ <a href="#" class="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 text-sm">
266
+ <i class="fas fa-star text-gray-500 dark:text-gray-400"></i>
267
+ <span>Favorites</span>
268
+ </a>
269
+ <a href="#" class="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 text-sm">
270
+ <i class="fas fa-trash text-gray-500 dark:text-gray-400"></i>
271
+ <span>Trash</span>
272
+ </a>
273
+ </div>
274
+ </div>
275
+ </div>
276
+ <div class="p-3 border-t border-gray-200 dark:border-gray-700">
277
+ <div class="flex items-center gap-3">
278
+ <div class="w-8 h-8 rounded-full bg-primary-100 dark:bg-primary-900 flex items-center justify-center text-primary-600 dark:text-primary-300">
279
+ <i class="fas fa-user"></i>
280
+ </div>
281
+ <div class="flex-1">
282
+ <div class="text-sm font-medium">Guest User</div>
283
+ <div class="text-xs text-gray-500 dark:text-gray-400">Free Plan</div>
284
+ </div>
285
+ <button id="theme-toggle" class="p-1 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
286
+ <i class="fas fa-moon dark:hidden"></i>
287
+ <i class="fas fa-sun hidden dark:block"></i>
288
+ </button>
289
  </div>
290
  </div>
291
  </div>
292
+
293
+ <!-- Main Content -->
294
+ <div class="flex-1 flex flex-col overflow-hidden">
295
+ <!-- Top Bar -->
296
+ <div class="bg-white dark:bg-dark-800 border-b border-gray-200 dark:border-gray-700 p-3 flex items-center justify-between">
297
+ <div class="flex items-center gap-2">
298
+ <button id="sidebar-toggle" class="md:hidden p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
299
+ <i class="fas fa-bars"></i>
300
+ </button>
301
+ <div id="breadcrumbs" class="flex items-center gap-1 text-sm">
302
+ <!-- Breadcrumbs will be loaded here -->
303
+ </div>
304
+ </div>
305
+ <div class="flex items-center gap-2">
306
+ <button id="search-button" class="p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
307
+ <i class="fas fa-search"></i>
308
+ </button>
309
+ <button id="share-button" class="p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
310
+ <i class="fas fa-share-alt"></i>
311
+ </button>
312
+ <button id="more-options" class="p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
313
+ <i class="fas fa-ellipsis-h"></i>
314
+ </button>
315
+ </div>
316
+ </div>
317
+
318
+ <!-- Editor Area -->
319
+ <div class="flex-1 overflow-auto p-4 md:p-6">
320
+ <div id="editor" class="max-w-4xl mx-auto prose dark:prose-invert">
321
+ <div id="editor-content" class="editor-content" contenteditable="true">
322
+ <!-- Editor content will be loaded here -->
323
+ </div>
324
+ </div>
325
+ </div>
326
+
327
+ <!-- Floating Action Buttons -->
328
+ <div class="fixed bottom-6 right-6 flex flex-col gap-3">
329
+ <div class="tooltip">
330
+ <button id="add-block" class="w-12 h-12 rounded-full bg-primary-500 text-white flex items-center justify-center shadow-lg hover:bg-primary-600 transition-colors">
331
+ <i class="fas fa-plus"></i>
332
+ </button>
333
+ <span class="tooltip-text">Add block</span>
334
+ </div>
335
+ <div class="tooltip">
336
+ <button id="save-button" class="w-12 h-12 rounded-full bg-green-500 text-white flex items-center justify-center shadow-lg hover:bg-green-600 transition-colors">
337
+ <i class="fas fa-save"></i>
338
+ </button>
339
+ <span class="tooltip-text">Save changes</span>
340
  </div>
 
341
  </div>
342
  </div>
343
  </div>
344
 
345
+ <!-- Add Block Menu -->
346
+ <div id="block-menu" class="hidden fixed z-50 bg-white dark:bg-dark-800 rounded-lg shadow-xl w-64 border border-gray-200 dark:border-gray-700 overflow-hidden">
347
+ <div class="p-2 border-b border-gray-200 dark:border-gray-700">
348
+ <div class="relative">
349
+ <input type="text" placeholder="Search blocks..." class="w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-primary-500">
350
+ <i class="fas fa-search absolute right-3 top-2.5 text-gray-400"></i>
351
+ </div>
352
+ </div>
353
+ <div class="overflow-y-auto max-h-96">
354
+ <div class="p-1">
355
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 px-2 py-1">Basic Blocks</div>
356
+ <button class="block-menu-item" data-type="text">
357
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
358
+ <i class="fas fa-align-left text-gray-500 dark:text-gray-400"></i>
359
+ </div>
360
+ <span>Text</span>
361
+ </button>
362
+ <button class="block-menu-item" data-type="heading1">
363
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
364
+ <i class="fas fa-heading text-gray-500 dark:text-gray-400"></i>
365
+ </div>
366
+ <span>Heading 1</span>
367
+ </button>
368
+ <button class="block-menu-item" data-type="heading2">
369
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
370
+ <i class="fas fa-heading text-gray-500 dark:text-gray-400" style="font-size: 0.8em;"></i>
371
+ </div>
372
+ <span>Heading 2</span>
373
+ </button>
374
+ <button class="block-menu-item" data-type="heading3">
375
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
376
+ <i class="fas fa-heading text-gray-500 dark:text-gray-400" style="font-size: 0.6em;"></i>
377
+ </div>
378
+ <span>Heading 3</span>
379
+ </button>
380
+ <button class="block-menu-item" data-type="bullet-list">
381
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
382
+ <i class="fas fa-list-ul text-gray-500 dark:text-gray-400"></i>
383
+ </div>
384
+ <span>Bulleted list</span>
385
+ </button>
386
+ <button class="block-menu-item" data-type="number-list">
387
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
388
+ <i class="fas fa-list-ol text-gray-500 dark:text-gray-400"></i>
389
+ </div>
390
+ <span>Numbered list</span>
391
+ </button>
392
+ <button class="block-menu-item" data-type="to-do">
393
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
394
+ <i class="fas fa-check-square text-gray-500 dark:text-gray-400"></i>
395
+ </div>
396
+ <span>To-do list</span>
397
+ </button>
398
+ <button class="block-menu-item" data-type="quote">
399
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
400
+ <i class="fas fa-quote-right text-gray-500 dark:text-gray-400"></i>
401
+ </div>
402
+ <span>Quote</span>
403
+ </button>
404
+ <button class="block-menu-item" data-type="divider">
405
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
406
+ <i class="fas fa-minus text-gray-500 dark:text-gray-400"></i>
407
+ </div>
408
+ <span>Divider</span>
409
  </button>
 
 
 
410
  </div>
411
+ <div class="p-1">
412
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 px-2 py-1">Media</div>
413
+ <button class="block-menu-item" data-type="image">
414
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
415
+ <i class="fas fa-image text-gray-500 dark:text-gray-400"></i>
416
+ </div>
417
+ <span>Image</span>
418
+ </button>
419
+ <button class="block-menu-item" data-type="video">
420
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
421
+ <i class="fas fa-video text-gray-500 dark:text-gray-400"></i>
422
+ </div>
423
+ <span>Video</span>
424
+ </button>
425
+ <button class="block-menu-item" data-type="file">
426
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
427
+ <i class="fas fa-file text-gray-500 dark:text-gray-400"></i>
428
+ </div>
429
+ <span>File</span>
430
  </button>
431
  </div>
432
+ <div class="p-1">
433
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 px-2 py-1">Advanced</div>
434
+ <button class="block-menu-item" data-type="code">
435
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
436
+ <i class="fas fa-code text-gray-500 dark:text-gray-400"></i>
437
+ </div>
438
+ <span>Code</span>
439
+ </button>
440
+ <button class="block-menu-item" data-type="table">
441
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
442
+ <i class="fas fa-table text-gray-500 dark:text-gray-400"></i>
443
+ </div>
444
+ <span>Table</span>
445
+ </button>
446
+ <button class="block-menu-item" data-type="page">
447
+ <div class="w-6 h-6 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
448
+ <i class="fas fa-file-alt text-gray-500 dark:text-gray-400"></i>
449
+ </div>
450
+ <span>Page</span>
451
+ </button>
452
  </div>
453
  </div>
454
  </div>
455
 
456
+ <!-- Formatting Toolbar -->
457
+ <div id="formatting-toolbar" class="hidden fixed z-40 bg-white dark:bg-dark-800 rounded-md shadow-lg border border-gray-200 dark:border-gray-700 p-1 flex items-center gap-1">
458
+ <button class="format-button" data-command="bold" title="Bold (Ctrl+B)">
459
  <i class="fas fa-bold"></i>
460
  </button>
461
+ <button class="format-button" data-command="italic" title="Italic (Ctrl+I)">
462
  <i class="fas fa-italic"></i>
463
  </button>
464
+ <button class="format-button" data-command="underline" title="Underline (Ctrl+U)">
465
  <i class="fas fa-underline"></i>
466
  </button>
467
+ <button class="format-button" data-command="strikeThrough" title="Strikethrough (Ctrl+Shift+X)">
468
  <i class="fas fa-strikethrough"></i>
469
  </button>
470
  <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>
471
+ <button class="format-button" data-command="justifyLeft" title="Align left">
472
+ <i class="fas fa-align-left"></i>
473
  </button>
474
+ <button class="format-button" data-command="justifyCenter" title="Align center">
475
+ <i class="fas fa-align-center"></i>
476
  </button>
477
+ <button class="format-button" data-command="justifyRight" title="Align right">
478
+ <i class="fas fa-align-right"></i>
479
  </button>
480
  <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>
481
+ <button class="format-button" data-command="insertUnorderedList" title="Bullet list">
482
  <i class="fas fa-list-ul"></i>
483
  </button>
484
+ <button class="format-button" data-command="insertOrderedList" title="Numbered list">
485
  <i class="fas fa-list-ol"></i>
486
  </button>
 
 
 
487
  <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>
488
+ <button class="format-button" data-command="createLink" title="Create link">
489
+ <i class="fas fa-link"></i>
490
  </button>
491
+ <button class="format-button" data-command="unlink" title="Remove link">
492
+ <i class="fas fa-unlink"></i>
493
+ </button>
494
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>
495
+ <button class="format-button" data-command="undo" title="Undo (Ctrl+Z)">
496
+ <i class="fas fa-undo"></i>
497
  </button>
498
+ <button class="format-button" data-command="redo" title="Redo (Ctrl+Y)">
499
+ <i class="fas fa-redo"></i>
500
  </button>
501
  </div>
502
 
503
+ <!-- Image Upload Modal -->
504
+ <div id="image-upload-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
505
+ <div class="bg-white dark:bg-dark-800 rounded-lg shadow-xl w-full max-w-md p-4">
506
+ <div class="flex items-center justify-between mb-4">
507
+ <h3 class="text-lg font-medium">Upload Image</h3>
508
+ <button id="close-image-modal" class="p-1 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
509
+ <i class="fas fa-times"></i>
510
+ </button>
511
+ </div>
512
+ <div class="mb-4">
513
+ <div class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-4 text-center">
514
+ <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-2"></i>
515
+ <p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Drag & drop your image here or click to browse</p>
516
+ <input type="file" id="image-input" accept="image/*" class="hidden">
517
+ <button id="browse-button" class="px-4 py-2 bg-primary-500 text-white rounded-md hover:bg-primary-600">
518
+ Browse Files
519
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  </div>
521
+ </div>
522
+ <div class="mb-4">
523
+ <label for="image-url" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Or enter image URL</label>
524
+ <input type="text" id="image-url" placeholder="https://example.com/image.jpg" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-gray-700">
525
+ </div>
526
+ <div class="flex justify-end gap-2">
527
+ <button id="cancel-image-upload" class="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md">
528
+ Cancel
529
+ </button>
530
+ <button id="confirm-image-upload" class="px-4 py-2 bg-primary-500 text-white rounded-md hover:bg-primary-600">
531
+ Insert Image
532
+ </button>
533
+ </div>
534
+ </div>
535
+ </div>
536
+
537
+ <!-- Add Page Modal -->
538
+ <div id="add-page-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
539
+ <div class="bg-white dark:bg-dark-800 rounded-lg shadow-xl w-full max-w-md p-4">
540
+ <div class="flex items-center justify-between mb-4">
541
+ <h3 class="text-lg font-medium">Create New Page</h3>
542
+ <button id="close-page-modal" class="p-1 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
543
+ <i class="fas fa-times"></i>
544
+ </button>
545
+ </div>
546
+ <div class="mb-4">
547
+ <label for="page-title" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Page Title</label>
548
+ <input type="text" id="page-title" placeholder="Untitled" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-gray-700">
549
+ </div>
550
+ <div class="mb-4">
551
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Parent Page</label>
552
+ <select id="parent-page" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-gray-700">
553
+ <option value="">No parent (top level)</option>
554
+ <!-- Parent pages will be populated here -->
555
+ </select>
556
+ </div>
557
+ <div class="flex justify-end gap-2">
558
+ <button id="cancel-page-add" class="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md">
559
+ Cancel
560
+ </button>
561
+ <button id="confirm-page-add" class="px-4 py-2 bg-primary-500 text-white rounded-md hover:bg-primary-600">
562
+ Create Page
563
+ </button>
564
+ </div>
565
  </div>
566
  </div>
567
 
568
  <script>
569
+ // State management
570
+ const state = {
571
+ currentPageId: null,
572
+ pages: [],
573
+ showFormattingToolbar: false,
574
+ toolbarPosition: { x: 0, y: 0 },
575
+ blockMenuPosition: { x: 0, y: 0 },
576
+ lastSelection: null
577
+ };
 
 
 
 
578
 
579
+ // DOM Elements
580
+ const elements = {
581
+ editorContent: document.getElementById('editor-content'),
582
+ pageList: document.getElementById('page-list'),
583
+ breadcrumbs: document.getElementById('breadcrumbs'),
584
+ sidebar: document.querySelector('.sidebar'),
585
+ sidebarToggle: document.getElementById('sidebar-toggle'),
586
+ sidebarClose: document.getElementById('sidebar-close'),
587
+ themeToggle: document.getElementById('theme-toggle'),
588
+ addBlock: document.getElementById('add-block'),
589
+ blockMenu: document.getElementById('block-menu'),
590
+ formattingToolbar: document.getElementById('formatting-toolbar'),
591
+ formatButtons: document.querySelectorAll('.format-button'),
592
+ blockMenuItems: document.querySelectorAll('.block-menu-item'),
593
+ imageUploadModal: document.getElementById('image-upload-modal'),
594
+ imageInput: document.getElementById('image-input'),
595
+ browseButton: document.getElementById('browse-button'),
596
+ imageUrl: document.getElementById('image-url'),
597
+ confirmImageUpload: document.getElementById('confirm-image-upload'),
598
+ cancelImageUpload: document.getElementById('cancel-image-upload'),
599
+ closeImageModal: document.getElementById('close-image-modal'),
600
+ addPageButton: document.getElementById('add-page'),
601
+ addPageModal: document.getElementById('add-page-modal'),
602
+ pageTitle: document.getElementById('page-title'),
603
+ parentPage: document.getElementById('parent-page'),
604
+ confirmPageAdd: document.getElementById('confirm-page-add'),
605
+ cancelPageAdd: document.getElementById('cancel-page-add'),
606
+ closePageModal: document.getElementById('close-page-modal'),
607
+ saveButton: document.getElementById('save-button')
608
+ };
609
 
610
+ // Initialize the app
611
+ function init() {
612
+ loadPages();
613
+ setupEventListeners();
614
+ updateUI();
615
+
616
+ // Create a default page if none exists
617
+ if (state.pages.length === 0) {
618
+ createPage('Welcome to Notion Lite', '');
619
+ } else {
620
+ loadPage(state.pages[0].id);
621
+ }
622
+ }
623
 
624
+ // Load pages from local storage
625
+ function loadPages() {
626
+ const savedPages = localStorage.getItem('notion-lite-pages');
627
+ if (savedPages) {
628
+ state.pages = JSON.parse(savedPages);
629
+ renderPageList();
630
+ }
631
+ }
632
 
633
+ // Save pages to local storage
634
+ function savePages() {
635
+ localStorage.setItem('notion-lite-pages', JSON.stringify(state.pages));
636
+ }
637
 
638
+ // Create a new page
639
+ function createPage(title, parentId) {
640
+ const newPage = {
641
+ id: Date.now().toString(),
642
+ title: title,
643
+ parentId: parentId || null,
644
+ content: '<h1>' + title + '</h1><p>Start writing here...</p>',
645
+ createdAt: new Date().toISOString(),
646
+ updatedAt: new Date().toISOString()
647
+ };
648
+
649
+ state.pages.push(newPage);
650
+ savePages();
651
+ renderPageList();
652
+
653
+ if (!parentId) {
654
+ loadPage(newPage.id);
655
+ }
656
+
657
+ return newPage;
658
+ }
659
 
660
+ // Load a page into the editor
661
+ function loadPage(pageId) {
662
+ const page = state.pages.find(p => p.id === pageId);
663
+ if (!page) return;
664
+
665
+ state.currentPageId = pageId;
666
+ elements.editorContent.innerHTML = page.content;
667
+
668
+ // Update breadcrumbs
669
+ updateBreadcrumbs(pageId);
670
+
671
+ // Update UI
672
+ updateUI();
673
+ }
674
 
675
+ // Save the current page
676
+ function saveCurrentPage() {
677
+ if (!state.currentPageId) return;
678
+
679
+ const pageIndex = state.pages.findIndex(p => p.id === state.currentPageId);
680
+ if (pageIndex === -1) return;
681
+
682
+ state.pages[pageIndex].content = elements.editorContent.innerHTML;
683
+ state.pages[pageIndex].updatedAt = new Date().toISOString();
684
+ savePages();
685
+
686
+ // Show save confirmation
687
+ const saveButton = elements.saveButton;
688
+ saveButton.innerHTML = '<i class="fas fa-check"></i>';
689
+ saveButton.classList.remove('bg-green-500', 'hover:bg-green-600');
690
+ saveButton.classList.add('bg-green-600');
691
+
692
+ setTimeout(() => {
693
+ saveButton.innerHTML = '<i class="fas fa-save"></i>';
694
+ saveButton.classList.remove('bg-green-600');
695
+ saveButton.classList.add('bg-green-500', 'hover:bg-green-600');
696
+ }, 2000);
697
+ }
698
 
699
+ // Update breadcrumbs
700
+ function updateBreadcrumbs(pageId) {
701
+ const page = state.pages.find(p => p.id === pageId);
702
+ if (!page) return;
703
+
704
+ let breadcrumbs = [];
705
+ let currentPage = page;
706
+
707
+ while (currentPage) {
708
+ breadcrumbs.unshift(currentPage);
709
+ currentPage = currentPage.parentId ? state.pages.find(p => p.id === currentPage.parentId) : null;
710
+ }
711
+
712
+ elements.breadcrumbs.innerHTML = '';
713
+
714
+ breadcrumbs.forEach((crumb, index) => {
715
+ const link = document.createElement('a');
716
+ link.href = '#';
717
+ link.className = 'hover:text-primary-500 dark:hover:text-primary-400';
718
+ link.textContent = crumb.title;
719
+ link.addEventListener('click', (e) => {
720
+ e.preventDefault();
721
+ loadPage(crumb.id);
722
+ });
723
+
724
+ elements.breadcrumbs.appendChild(link);
725
+
726
+ if (index < breadcrumbs.length - 1) {
727
+ const separator = document.createElement('span');
728
+ separator.className = 'text-gray-400';
729
+ separator.textContent = ' / ';
730
+ elements.breadcrumbs.appendChild(separator);
731
+ }
732
+ });
733
+ }
734
 
735
+ // Render the page list in the sidebar
736
+ function renderPageList() {
737
+ elements.pageList.innerHTML = '';
738
+
739
+ // Top-level pages
740
+ const topLevelPages = state.pages.filter(page => !page.parentId);
741
+
742
+ topLevelPages.forEach(page => {
743
+ renderPageListItem(page);
744
+ });
745
+ }
746
 
747
+ // Render a single page list item (recursive for children)
748
+ function renderPageListItem(page, depth = 0) {
749
+ const item = document.createElement('div');
750
+ item.className = `group flex items-center justify-between rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 ${state.currentPageId === page.id ? 'bg-gray-100 dark:bg-gray-700' : ''}`;
751
+
752
+ const leftSide = document.createElement('div');
753
+ leftSide.className = 'flex items-center flex-1 min-w-0';
754
+ leftSide.style.paddingLeft = `${depth * 12}px`;
755
+
756
+ const link = document.createElement('a');
757
+ link.href = '#';
758
+ link.className = 'flex items-center gap-2 py-1.5 px-2 flex-1 min-w-0';
759
+ link.innerHTML = `
760
+ <i class="fas fa-file-alt text-gray-500 dark:text-gray-400"></i>
761
+ <span class="truncate">${page.title}</span>
762
+ `;
763
+ link.addEventListener('click', (e) => {
764
+ e.preventDefault();
765
+ loadPage(page.id);
766
+ });
767
+
768
+ const rightSide = document.createElement('div');
769
+ rightSide.className = 'opacity-0 group-hover:opacity-100 pr-2';
770
+
771
+ const addButton = document.createElement('button');
772
+ addButton.className = 'p-1 rounded-md hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-500 dark:text-gray-400';
773
+ addButton.innerHTML = '<i class="fas fa-plus text-xs"></i>';
774
+ addButton.addEventListener('click', (e) => {
775
+ e.stopPropagation();
776
+ showAddPageModal(page.id);
777
+ });
778
+
779
+ rightSide.appendChild(addButton);
780
+
781
+ leftSide.appendChild(link);
782
+ item.appendChild(leftSide);
783
+ item.appendChild(rightSide);
784
+ elements.pageList.appendChild(item);
785
+
786
+ // Child pages
787
+ const childPages = state.pages.filter(p => p.parentId === page.id);
788
+ childPages.forEach(child => {
789
+ renderPageListItem(child, depth + 1);
790
+ });
791
+ }
792
 
793
+ // Update UI based on current state
794
+ function updateUI() {
795
+ // Highlight current page in sidebar
796
+ document.querySelectorAll('#page-list a').forEach(link => {
797
+ link.parentElement.parentElement.classList.remove('bg-gray-100', 'dark:bg-gray-700');
798
+ });
799
+
800
+ const currentLink = document.querySelector(`#page-list a[href="#"]`);
801
+ if (currentLink) {
802
+ currentLink.parentElement.parentElement.classList.add('bg-gray-100', 'dark:bg-gray-700');
803
+ }
804
+ }
805
 
806
+ // Show the add block menu
807
+ function showBlockMenu(x, y) {
808
+ state.blockMenuPosition = { x, y };
809
+ elements.blockMenu.style.left = `${x}px`;
810
+ elements.blockMenu.style.top = `${y}px`;
811
+ elements.blockMenu.classList.remove('hidden');
812
+
813
+ // Focus search input if available
814
+ const searchInput = elements.blockMenu.querySelector('input');
815
+ if (searchInput) {
816
+ searchInput.focus();
817
+ }
818
+ }
819
 
820
+ // Hide the add block menu
821
+ function hideBlockMenu() {
822
+ elements.blockMenu.classList.add('hidden');
823
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
824
 
825
+ // Show the formatting toolbar
826
+ function showFormattingToolbar(x, y) {
827
+ state.showFormattingToolbar = true;
828
+ state.toolbarPosition = { x, y };
829
+ elements.formattingToolbar.style.left = `${x}px`;
830
+ elements.formattingToolbar.style.top = `${y}px`;
831
+ elements.formattingToolbar.classList.remove('hidden');
832
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
833
 
834
+ // Hide the formatting toolbar
835
+ function hideFormattingToolbar() {
836
+ state.showFormattingToolbar = false;
837
+ elements.formattingToolbar.classList.add('hidden');
838
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
839
 
840
+ // Show image upload modal
841
+ function showImageUploadModal() {
842
+ elements.imageUploadModal.classList.remove('hidden');
843
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
844
 
845
+ // Hide image upload modal
846
+ function hideImageUploadModal() {
847
+ elements.imageUploadModal.classList.add('hidden');
848
+ elements.imageInput.value = '';
849
+ elements.imageUrl.value = '';
850
+ }
 
 
 
 
 
 
 
 
 
 
 
 
851
 
852
+ // Show add page modal
853
+ function showAddPageModal(parentId = null) {
854
+ elements.addPageModal.classList.remove('hidden');
855
+ elements.parentPage.value = parentId || '';
856
+ }
 
 
 
 
 
 
 
857
 
858
+ // Hide add page modal
859
+ function hideAddPageModal() {
860
+ elements.addPageModal.classList.add('hidden');
861
+ elements.pageTitle.value = '';
862
+ elements.parentPage.value = '';
863
+ }
864
 
865
+ // Insert a block at the current selection
866
+ function insertBlock(type) {
867
+ const selection = window.getSelection();
868
+ if (!selection.rangeCount) return;
869
+
870
+ const range = selection.getRangeAt(0);
871
+ const block = document.createElement('div');
872
+ block.className = 'mb-4';
873
+
874
+ switch (type) {
875
+ case 'heading1':
876
+ block.innerHTML = '<h1>Heading 1</h1>';
877
+ break;
878
+ case 'heading2':
879
+ block.innerHTML = '<h2>Heading 2</h2>';
880
+ break;
881
+ case 'heading3':
882
+ block.innerHTML = '<h3>Heading 3</h3>';
883
+ break;
884
+ case 'bullet-list':
885
+ block.innerHTML = '<ul><li>List item</li></ul>';
886
+ break;
887
+ case 'number-list':
888
+ block.innerHTML = '<ol><li>List item</li></ol>';
889
+ break;
890
+ case 'to-do':
891
+ block.innerHTML = `
892
+ <div class="to-do-item">
893
+ <input type="checkbox" class="to-do-checkbox">
894
+ <div class="to-do-content" contenteditable="true">Todo item</div>
895
+ </div>
896
+ `;
897
+ break;
898
+ case 'quote':
899
+ block.innerHTML = '<blockquote>Quote</blockquote>';
900
+ break;
901
+ case 'divider':
902
+ block.innerHTML = '<hr class="border-t border-gray-300 dark:border-gray-600 my-4">';
903
+ break;
904
+ case 'image':
905
+ block.innerHTML = `
906
+ <div class="image-upload-wrapper">
907
+ <img src="https://via.placeholder.com/600x400?text=Click+to+upload+image" alt="Placeholder image" class="image-upload">
908
+ <div class="image-upload-actions">
909
+ <button class="p-1 bg-white dark:bg-gray-700 rounded-md shadow hover:bg-gray-100 dark:hover:bg-gray-600">
910
+ <i class="fas fa-edit text-gray-700 dark:text-gray-300"></i>
911
+ </button>
912
+ <button class="p-1 bg-white dark:bg-gray-700 rounded-md shadow hover:bg-gray-100 dark:hover:bg-gray-600">
913
+ <i class="fas fa-trash text-gray-700 dark:text-gray-300"></i>
914
+ </button>
915
+ </div>
916
+ </div>
917
+ `;
918
+ break;
919
+ case 'code':
920
+ block.innerHTML = '<pre><code>// Your code here</code></pre>';
921
+ break;
922
+ case 'table':
923
+ block.innerHTML = `
924
+ <div class="table-wrapper">
925
+ <table>
926
+ <thead>
927
+ <tr>
928
+ <th>Header 1</th>
929
+ <th>Header 2</th>
930
+ <th>Header 3</th>
931
+ </tr>
932
+ </thead>
933
+ <tbody>
934
+ <tr>
935
+ <td>Cell 1</td>
936
+ <td>Cell 2</td>
937
+ <td>Cell 3</td>
938
+ </tr>
939
+ <tr>
940
+ <td>Cell 4</td>
941
+ <td>Cell 5</td>
942
+ <td>Cell 6</td>
943
+ </tr>
944
+ </tbody>
945
+ </table>
946
+ </div>
947
+ `;
948
+ break;
949
+ default:
950
+ block.innerHTML = '<p>Text block</p>';
951
+ }
952
+
953
+ range.insertNode(block);
954
+
955
+ // Move cursor inside the new block
956
+ const editableElement = block.querySelector('[contenteditable="true"]') || block;
957
+ const newRange = document.createRange();
958
+ newRange.selectNodeContents(editableElement);
959
+ newRange.collapse(false);
960
+ selection.removeAllRanges();
961
+ selection.addRange(newRange);
962
+ editableElement.focus();
963
+
964
+ hideBlockMenu();
965
+ }
966
 
967
+ // Execute a formatting command
968
+ function executeFormatCommand(command, value = null) {
969
+ document.execCommand(command, false, value);
970
+ elements.editorContent.focus();
971
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
972
 
973
+ // Setup event listeners
974
+ function setupEventListeners() {
975
+ // Editor content events
976
+ elements.editorContent.addEventListener('input', () => {
977
+ // Auto-save after a delay
978
+ clearTimeout(window.saveTimeout);
979
+ window.saveTimeout = setTimeout(saveCurrentPage, 1000);
980
+ });
981
+
982
+ elements.editorContent.addEventListener('click', () => {
983
+ elements.editorContent.focus();
984
+ });
985
+
986
+ elements.editorContent.addEventListener('keydown', (e) => {
987
+ // Save on Ctrl+S
988
+ if (e.ctrlKey && e.key === 's') {
989
+ e.preventDefault();
990
+ saveCurrentPage();
991
  }
992
+
993
+ // Show block menu on / command
994
+ if (e.key === '/' && elements.editorContent === document.activeElement) {
995
+ e.preventDefault();
996
+ const range = window.getSelection().getRangeAt(0);
997
+ const rect = range.getBoundingClientRect();
998
+ showBlockMenu(rect.left, rect.bottom);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
999
  }
1000
+ });
1001
+
1002
+ elements.editorContent.addEventListener('mouseup', (e) => {
1003
+ const selection = window.getSelection();
1004
+ if (selection.toString().length > 0) {
1005
+ const range = selection.getRangeAt(0);
1006
+ const rect = range.getBoundingClientRect();
1007
+ showFormattingToolbar(rect.left, rect.top - 40);
1008
+ state.lastSelection = selection;
1009
+ } else {
1010
+ hideFormattingToolbar();
 
 
 
1011
  }
1012
+ });
1013
+
1014
+ // Sidebar events
1015
+ elements.sidebarToggle.addEventListener('click', () => {
1016
+ elements.sidebar.classList.add('sidebar-open');
1017
+ });
1018
+
1019
+ elements.sidebarClose.addEventListener('click', () => {
1020
+ elements.sidebar.classList.remove('sidebar-open');
1021
+ });
1022
+
1023
+ // Theme toggle
1024
+ elements.themeToggle.addEventListener('click', () => {
1025
+ document.documentElement.classList.toggle('dark');
1026
+ localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
1027
+ });
1028
+
1029
+ // Add block button
1030
+ elements.addBlock.addEventListener('click', () => {
1031
+ const rect = elements.addBlock.getBoundingClientRect();
1032
+ showBlockMenu(rect.left - 200, rect.top - 10);
1033
+ });
1034
+
1035
+ // Block menu items
1036
+ elements.blockMenuItems.forEach(item => {
1037
+ item.addEventListener('click', (e) => {
1038
+ e.preventDefault();
1039
+ const type = item.dataset.type;
1040
+ insertBlock(type);
1041
+ });
1042
+ });
1043
+
1044
+ // Format buttons
1045
+ elements.formatButtons.forEach(button => {
1046
+ button.addEventListener('click', () => {
1047
+ executeFormatCommand(button.dataset.command);
1048
+ });
1049
+ });
1050
+
1051
+ // Click outside to hide menus
1052
+ document.addEventListener('click', (e) => {
1053
+ if (!elements.blockMenu.contains(e.target) && e.target !== elements.addBlock) {
1054
+ hideBlockMenu();
1055
  }
1056
+
1057
+ if (!elements.formattingToolbar.contains(e.target) &&
1058
+ !(state.lastSelection && state.lastSelection.toString().length > 0)) {
1059
+ hideFormattingToolbar();
1060
+ }
1061
+ });
1062
+
1063
+ // Image upload modal events
1064
+ elements.browseButton.addEventListener('click', () => {
1065
+ elements.imageInput.click();
1066
+ });
1067
+
1068
+ elements.imageInput.addEventListener('change', (e) => {
1069
+ const file = e.target.files[0];
1070
+ if (file) {
1071
+ const reader = new FileReader();
1072
+ reader.onload = (event) => {
1073
+ elements.imageUrl.value = event.target.result;
1074
+ };
1075
+ reader.readAsDataURL(file);
1076
+ }
1077
+ });
1078
+
1079
+ elements.confirmImageUpload.addEventListener('click', () => {
1080
+ const imageUrl = elements.imageUrl.value.trim();
1081
+ if (imageUrl) {
1082
+ const selection = window.getSelection();
1083
+ if (selection.rangeCount) {
1084
+ const range = selection.getRangeAt(0);
1085
+ const img = document.createElement('img');
1086
+ img.src = imageUrl;
1087
+ img.className = 'rounded-md max-w-full h-auto';
1088
+ range.insertNode(img);
1089
+ hideImageUploadModal();
1090
  }
1091
  }
1092
+ });
1093
+
1094
+ elements.cancelImageUpload.addEventListener('click', hideImageUploadModal);
1095
+ elements.closeImageModal.addEventListener('click', hideImageUploadModal);
1096
+
1097
+ // Add page modal events
1098
+ elements.addPageButton.addEventListener('click', showAddPageModal);
1099
+
1100
+ elements.confirmPageAdd.addEventListener('click', () => {
1101
+ const title = elements.pageTitle.value.trim() || 'Untitled';
1102
+ const parentId = elements.parentPage.value || null;
1103
+ createPage(title, parentId);
1104
+ hideAddPageModal();
1105
+ });
1106
+
1107
+ elements.cancelPageAdd.addEventListener('click', hideAddPageModal);
1108
+ elements.closePageModal.addEventListener('click', hideAddPageModal);
1109
+
1110
+ // Save button
1111
+ elements.saveButton.addEventListener('click', saveCurrentPage);
1112
+
1113
+ // Check for saved dark mode preference
1114
+ if (localStorage.getItem('darkMode') === 'true') {
1115
+ document.documentElement.classList.add('dark');
1116
+ } else if (localStorage.getItem('darkMode') === 'false') {
1117
+ document.documentElement.classList.remove('dark');
1118
+ } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
1119
+ document.documentElement.classList.add('dark');
1120
  }
1121
+ }
1122
 
1123
+ // Initialize the app when DOM is loaded
1124
+ document.addEventListener('DOMContentLoaded', init);
 
1125
  </script>
1126
  <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=Harry00/notion" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1127
  </html>