← Notes results

Notes

bonsai 27b · composer

Initial view of Notes

Browser check: Artifact runtime error. This is a technical check, not a quality review.

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 41,039 bytes · SHA-256 22b7ab15e951
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Notes — Beautiful Note Taking App</title>
<style>
/* ── css: layout ── */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; background: #f0f2f5; color: #1a1a2e; }
.app-wrapper { display: flex; min-height: 100vh; }

/* Sidebar */
.sidebar { width: 240px; background: #ffffff; border-right: 1px solid #e2e8f0; display: flex; flex-direction: column; position: relative; z-index: 10; }
.sidebar-header { padding: 16px 20px; border-bottom: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: space-between; }
.logo { display: flex; align-items: center; gap: 10px; }
.logo-icon { font-size: 24px; }
.logo-text { font-size: 22px; font-weight: 700; color: #1a1a2e; }
.sidebar-actions { display: flex; gap: 8px; }
.sidebar-actions button { display: flex; align-items: center; gap: 8px; padding: 8px 14px; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s; }
.sidebar-header .btn-primary { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; }
.sidebar-header .btn-secondary { background: #f1f5f9; color: #334155; }

.sidebar-body { flex: 1; overflow-y: auto; padding: 12px 20px; }
.sidebar-footer { padding: 12px 20px; border-top: 1px solid #e2e8f0; }

/* Search */
.search-bar { display: flex; gap: 8px; padding: 12px 0; margin-bottom: 4px; }
.search-input-wrap { flex: 1; position: relative; }
.search-input-wrap svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: #94a3b8; }
.search-input { width: 100%; height: 36px; padding: 10px 12px 10px 38px; border: 1px solid #d1d5db; border-radius: 10px; font-size: 14px; background: #ffffff; color: #1a1a2e; outline: none; transition: border-color 0.2s; }
.search-input:focus { border-color: #6366f1; }
.search-filters { display: flex; gap: 4px; }
.filter-btn { padding: 6px 12px; border: none; border-radius: 20px; font-size: 13px; cursor: pointer; transition: all 0.2s; background: #f1f5f9; color: #64748b; }
.filter-btn.active { background: #6366f1; color: white; }
.filter-btn:hover:not(.active) { background: #e2e8f0; color: #334155; }

/* Note List */
.section-header { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.section-label { font-size: 13px; color: #64748b; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.section-count { font-size: 13px; color: #94a3b8; }
.note-list { list-style: none; }
.note-list li { display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; cursor: pointer; transition: background 0.15s; }
.note-list li:hover { background: #f1f5f9; }
.note-list li .note-color { width: 4px; height: 24px; background: #6366f1; border-radius: 2px; flex-shrink: 0; margin-top: 1px; }
.note-list li .note-info { flex: 1; min-width: 0; }
.note-list li .note-title { font-size: 14px; font-weight: 600; color: #1a1a2e; margin-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.note-list li .note-preview { font-size: 13px; color: #64748b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.note-list li .note-actions { display: flex; align-items: center; gap: 4px; }
.note-list li .note-star { color: #f59e0b; }
.note-list li .note-date { font-size: 11px; color: #94a3b8; margin-left: auto; }

/* Main Area */
.main-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.editor-header { padding: 20px 24px; border-bottom: 1px solid #e2e8f0; display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-shrink: 0; }
.editor-header .btn { display: flex; align-items: center; gap: 6px; padding: 6px 12px; border: 1px solid #d1d5db; border-radius: 8px; background: #ffffff; color: #334155; cursor: pointer; font-size: 13px; transition: all 0.2s; }
.editor-header .btn:hover { background: #f1f5f9; border-color: #94a3b8; }
.editor-header .btn-star { border-color: #f59e0b; color: #f59e0b; }
.editor-header .btn-star:hover { background: #fef3c7; }
.editor-header .btn-star .btn-icon svg { fill: #f59e0b; stroke: #f59e0b; }
.editor-header .btn-danger { border-color: #dc2626; color: #dc2626; }
.editor-header .btn-danger:hover { background: #fee2e2; }
.editor-header .btn-icon { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 8px; }
.editor-header .btn-icon svg { width: 18px; height: 18px; }
.header-content h1 { margin: 0; font-size: 28px; font-weight: 700; color: #1a1a2e; line-height: 1.2; }
.header-content { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.note-actions { display: flex; align-items: center; gap: 8px; }

/* Editor Content */
.editor-content { flex: 1; overflow-y: auto; padding: 24px; position: relative; }
.editor-content .markdown { word-break: break-word; }
.editor-content h1 { font-size: 28px; font-weight: 700; margin-bottom: 16px; color: #1a1a2e; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px; }
.editor-content h2 { font-size: 24px; font-weight: 600; margin: 24px 0 12px; color: #1a1a2e; }
.editor-content h3 { font-size: 20px; font-weight: 600; margin: 20px 0 12px; color: #1a1a2e; }
.editor-content h4 { font-size: 18px; font-weight: 600; margin: 16px 0 12px; color: #1a1a2e; }
.editor-content h5 { font-size: 16px; font-weight: 600; margin: 12px 0 8px; color: #1a1a2e; }
.editor-content h6 { font-size: 15px; font-weight: 600; margin: 10px 0 8px; color: #1a1a2e; }
.editor-content p { line-height: 1.7; margin-bottom: 12px; color: #334155; }
.editor-content blockquote { border-left: 4px solid #6366f1; padding: 0 16px; margin: 16px 0; color: #64748b; font-style: italic; }
.editor-content ul { margin: 12px 0; }
.editor-content ol { margin: 12px 0; }
.editor-content li { margin-bottom: 4px; }
.editor-content strong { font-weight: 600; }
.editor-content em { font-style: italic; }
.editor-content a { color: #6366f1; text-decoration: none; }
.editor-content a:hover { text-decoration: underline; }
.editor-content pre { background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; overflow-x: auto; margin: 12px 0; font-family: 'Fira Code', 'SF Mono', monospace; font-size: 13px; }
.editor-content code { background: #f1f5f9; padding: 2px 6px; border-radius: 4px; font-size: 13px; }
.editor-content table { border-collapse: collapse; margin: 12px 0; }
.editor-content th, .editor-content td { border: 1px solid #e2e8f0; padding: 8px 12px; text-align: left; }
.editor-content th { background: #f1f5f9; font-weight: 600; }
.editor-content tr:hover td { background: #f1f5f9; }
.editor-content .task { display: flex; align-items: center; gap: 8px; margin: 4px 0; cursor: pointer; }
.editor-content .task input[type="checkbox"] { width: 20px; height: 20px; accent-color: #6366f1; }
.editor-content .task:checked + span { text-decoration: line-through; color: #94a3b8; }

/* Footer */
.editor-footer { border-top: 1px solid #e2e8f0; padding: 12px 24px; background: #ffffff; display: flex; align-items: center; gap: 16px; flex-shrink: 0; }
.editor-footer .btn { display: flex; align-items: center; gap: 6px; padding: 6px 12px; border: 1px solid #d1d5db; border-radius: 8px; background: #ffffff; color: #334155; cursor: pointer; font-size: 13px; transition: all 0.2s; }
.editor-footer .btn-icon { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 8px; }
.editor-footer .btn-icon svg { width: 18px; height: 18px; }
.editor-footer .btn:hover { background: #f1f5f9; border-color: #94a3b8; }
.editor-footer #footerSeparator { flex: 1; }
.editor-footer span { font-size: 13px; color: #64748b; }
.editor-footer #footerWordCount { font-weight: 600; color: #334155; }

/* Modal */
.modal { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 100; background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); opacity: 0; pointer-events: none; transition: opacity 0.2s; }
.modal.hidden { display: none; }
.modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); }
.modal-dialog { background: #ffffff; border-radius: 16px; padding: 24px; width: 90%; max-width: 400px; transform: scale(0.9); transition: transform 0.2s; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15); }
.modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; }
.modal-title { font-size: 20px; font-weight: 700; color: #1a1a2e; }
.modal-body { margin-bottom: 20px; }
.modal-body p { margin-bottom: 8px; color: #334155; }
.modal-subtext { font-size: 13px; color: #64748b; }
.modal-footer { display: flex; gap: 8px; justify-content: flex-end; }
.modal .btn { padding: 10px 20px; border: none; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
.modal .btn-close { background: #f1f5f9; color: #64748b; }
.modal .btn-close:hover { background: #e2e8f0; }
.modal .btn-primary { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; }
.modal .btn-secondary { background: #f1f5f9; color: #334155; }
.modal .btn-danger { background: #dc2626; color: white; }
.modal .btn:hover { opacity: 0.9; }

/* Form */
.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 13px; font-weight: 600; color: #334155; margin-bottom: 6px; }
.form-input { width: 100%; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 10px; font-size: 14px; background: #ffffff; color: #1a1a2e; outline: none; transition: border-color 0.2s; }
.form-input:focus { border-color: #6366f1; }
.color-picker { width: 36px; height: 36px; border-radius: 8px; border: 2px solid #d1d5db; cursor: pointer; }

/* Responsive */
@media (max-width: 768px) {
  .sidebar { display: none; }
  .main-area { flex-direction: column; }
}
</style>
</head>
<body>
<div id="app" class="app-wrapper">
  <!-- Sidebar -->
  <aside id="sidebar" class="sidebar" aria-label="Navigation">
    <div class="sidebar-header">
      <div class="logo">
        <div class="logo-icon">✏</div>
        <span class="logo-text">Notes</span>
      </div>
      <div class="sidebar-actions">
        <button id="newNoteBtn" class="btn btn-primary" aria-label="Create new note">
          <span class="btn-icon">+</span>
          New Note
        </button>
        <button id="showSidebarBtn" class="btn btn-secondary btn-icon" aria-label="Toggle sidebar">
          <span>☰</span>
        </button>
      </div>
    </div>
    <div class="sidebar-body">
      <div class="search-bar">
        <div class="search-input-wrap">
          <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="11" cy="11" r="8"></circle>
            <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
          </svg>
          <input id="searchInput" type="text" placeholder="Search notes..." class="search-input" aria-label="Search notes">
        </div>
        <div class="search-filters">
          <button id="filterAll" class="filter-btn active" data-filter="all">All</button>
          <button id="filterStarred" class="filter-btn" data-filter="starred">★ Starred</button>
          <button id="filterArchive" class="filter-btn" data-filter="archive">📁 Archive</button>
        </div>
      </div>
      <div class="section-header">
        <span class="section-label">Your Notes</span>
        <span class="section-count" id="sectionCount">0 notes</span>
      </div>
      <ul id="noteList" class="note-list" role="list">
        <!-- Notes rendered by JS -->
      </ul>
    </div>
    <div class="sidebar-footer">
      <div class="sidebar-footer-info">
        <span class="footer-label">Storage</span>
        <div class="footer-stats">
          <div class="stat" data-stat="total">
            <span class="stat-value" id="statTotal">0</span>
            <span class="stat-unit">notes</span>
          </div>
          <div class="stat" data-stat="starred">
            <span class="stat-value" id="statStarred">0</span>
            <span class="stat-unit">starred</span>
          </div>
        </div>
      </div>
    </div>
  </aside>

  <!-- Main Editor Area -->
  <div id="mainArea" class="main-area">
    <div class="editor-header">
      <button id="backBtn" class="btn btn-secondary btn-icon" aria-label="Go back">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <polyline points="9 18 15 12 9 6"></polyline>
        </svg>
      </button>
      <div class="header-content">
        <h1 id="noteTitle" class="note-title">Untitled Note</h1>
        <div class="note-actions">
          <button id="toggleStarBtn" class="btn btn-star" aria-label="Toggle star">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M12 2.082m0 13.838A9 9 0 0122 11.918V22a2 2 0 11-4 0v-7.082c0-5.509-4.452-10.827-10-10.827S2.548 16.471 0 22v-7.082a9.003 9.003 0 0112-10.827z"></path>
            </svg>
          </button>
          <button id="editNoteBtn" class="btn btn-secondary btn-icon" aria-label="Edit note">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7.082c0-5.509-4.452-10.827-10-10.827S2.548 16.471 0 22v-7.082a9.003 9.003 0 0112-10.827z"></path>
            </svg>
          </button>
          <button id="deleteNoteBtn" class="btn btn-danger btn-icon" aria-label="Delete note">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M19 7l-.863.863A2 2 0 0016 12h-2v-6a2 2 0 002-2z"></path>
              <path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
            </svg>
          </button>
        </div>
      </div>
    </div>
    <div id="editorContent" class="editor-content">
      <!-- Content rendered by JS -->
    </div>
    <div class="editor-footer">
      <div class="footer-bar">
        <button id="insertListBtn" class="btn btn-secondary btn-icon" aria-label="Insert unordered list">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M9 19l-1-1 5.5-5.5 1 1-4.5 4.5M18 10l1 1-5.5 5.5-1-1 4.5-4.5"></path>
            <path d="M9 19l-1-1 5.5-5.5 1 1-4.5 4.5M18 10l1 1-5.5 5.5-1-1 4.5-4.5"></path>
          </svg>
        </button>
        <button id="insertHeadingBtn" class="btn btn-secondary btn-icon" aria-label="Insert heading">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="12" y1="1" x2="12" y2="21"></line>
            <line x1="5.5" y1="1" x2="5.5" y2="21"></line>
            <line x1="12" y1="1" x2="12" y2="21"></line>
            <line x1="18.5" y1="1" x2="18.5" y2="21"></line>
          </svg>
        </button>
        <button id="insertImageBtn" class="btn btn-secondary btn-icon" aria-label="Insert image">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <rect x="3" y="3" width="18" height="18" rx="2"></rect>
            <circle cx="8" cy="8" r="2"></circle>
            <circle cx="16" cy="16" r="2"></circle>
            <path d="M19 9H5m14 0v6M5 9v6"></path>
          </svg>
        </button>
        <button id="insertCodeBtn" class="btn btn-secondary btn-icon" aria-label="Insert code block">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M9 10v4a2 2 0 002 2h8a2 2 0 002-2v-4M9 10l-1-1v4l-1 1M16 10l1-1v4l1-1"></path>
          </svg>
        </button>
        <button id="insertTaskBtn" class="btn btn-secondary btn-icon" aria-label="Insert task">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="12" cy="12" r="10"></circle>
            <line x1="12" y1="8" x2="16.35" y2="11.65"></line>
            <line x1="12" y1="16" x2="16.35" y2="19.35"></line>
          </svg>
        </button>
        <button id="insertTableBtn" class="btn btn-secondary btn-icon" aria-label="Insert table">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <rect x="3" y="3" width="18" height="18" rx="2"></rect>
            <line x1="9" y1="3" x2="9" y2="21"></line>
            <line x1="15" y1="3" x2="15" y2="21"></line>
            <line x1="3" y1="9" x2="21" y2="9"></line>
            <line x1="3" y1="15" x2="21" y2="15"></line>
          </svg>
        </button>
        <div id="footerSeparator"></div>
        <span id="footerStatus">Ready</span>
        <span id="footerWordCount">0 words</span>
      </div>
    </div>
  </div>

  <!-- Modal: New Note -->
  <div id="newNoteModal" class="modal hidden">
    <div class="modal-overlay" data-modal="newNoteModal"></div>
    <div class="modal-dialog" role="dialog" aria-modal="true">
      <div class="modal-header">
        <span class="modal-title">Create New Note</span>
        <button id="closeNewNoteModal" class="btn btn-close" aria-label="Close">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="18" y1="6" x2="6" y2="18"></line>
            <line x1="6" y1="6" x2="18" y2="18"></line>
          </svg>
        </button>
      </div>
      <div class="modal-body">
        <form id="newNoteForm">
          <div class="form-group">
            <label for="newNoteTitle">Title</label>
            <input id="newNoteTitle" type="text" placeholder="Enter note title..." class="form-input" required="">
          </div>
          <div class="form-group">
            <label for="newNoteColor">Cover Color</label>
            <input id="newNoteColor" type="color" class="form-input color-picker" value="#6366f1" style="width:36px;height:36px;border:none;height:36px;padding:0;">
          </div>
          <div class="form-group">
            <label for="newNoteDescription">Description</label>
            <textarea id="newNoteDescription" class="form-input" placeholder="Add a brief description..."></textarea>
          </div>
        </form>
        <div class="modal-footer">
          <button id="createNoteBtn" class="btn btn-primary">Create Note</button>
          <button id="cancelNewNoteBtn" class="btn btn-secondary">Cancel</button>
        </div>
      </div>
    </div>
  </div>

  <!-- Modal: Delete Confirmation -->
  <div id="deleteModal" class="modal hidden">
    <div class="modal-overlay" data-modal="deleteModal"></div>
    <div class="modal-dialog" role="dialog" aria-modal="true">
      <div class="modal-header">
        <span class="modal-title">Delete Note</span>
        <button id="closeDeleteModal" class="btn btn-close" aria-label="Close">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="18" y1="6" x2="6" y2="18"></line>
            <line x1="6" y1="6" x2="18" y2="18"></line>
          </svg>
        </button>
      </div>
      <div class="modal-body">
        <p class="modal-message" id="deleteModalMessage">Are you sure you want to delete this note?</p>
        <p class="modal-subtext" id="deleteModalSubtext">This action cannot be undone.</p>
      </div>
      <div class="modal-footer">
        <button id="confirmDeleteBtn" class="btn btn-danger">Delete</button>
        <button id="cancelDeleteBtn" class="btn btn-secondary">Cancel</button>
      </div>
    </div>
  </div>
</div>
<div id="app"></div>
<script>
'use strict';

// ── const: STORAGE_KEY ──
const STORAGE_KEY = 'notes_app_data';

// ── const: DEFAULT_NOTE ──
const DEFAULT_NOTE = {
  id: null,
  title: '',
  content: '',
  createdAt: Date.now(),
  updatedAt: Date.now(),
  isStarred: false,
  isArchive: false,
  color: '#6366f1'
};

// ── let: nextId ──
let nextId = 1;

// ── let: currentNoteId ──
let currentNoteId = null;

// ── let: notes ──
let notes = [];

// ── let: editingNote ──
let editingNote = null;

// ── function: loadNotes ──
function loadNotes() {
  try {
    const saved = localStorage.getItem(STORAGE_KEY);
    if (saved) {
      notes = JSON.parse(saved);
      nextId = Math.max(...notes.map(n => n.id), 0) + 1;
    }
  } catch (e) {
    console.error('Failed to load notes:', e);
  }
}

// ── function: saveNotes ──
function saveNotes() {
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(notes));
  } catch (e) {
    console.error('Failed to save notes:', e);
  }
}

// ── function: createNote ──
function createNote(note = DEFAULT_NOTE) {
  const id = nextId++;
  note.id = id;
  note.createdAt = Date.now();
  note.updatedAt = Date.now();
  notes.push(note);
  saveNotes();
  return note;
}

// ── function: getNote ──
function getNote(id) {
  return notes.find(n => n.id === id);
}

// ── function: updateNote ──
function updateNote(id, updates) {
  const note = getNote(id);
  if (note) {
    Object.assign(note, updates, { updatedAt: Date.now() });
    saveNotes();
    return note;
  }
  return null;
}

// ── function: deleteNote ──
function deleteNote(id) {
  notes = notes.filter(n => n.id !== id);
  saveNotes();
}

// ── function: getFilteredNotes ──
function getFilteredNotes(filter = 'all') {
  let result = [...notes];
  if (filter === 'starred') {
    result = result.filter(n => n.isStarred);
  } else if (filter === 'archive') {
    result = result.filter(n => n.isArchive);
  }
  return result;
}

// ── function: getSearchResults ──
function getSearchResults(query) {
  if (!query) return getFilteredNotes();
  const q = query.toLowerCase();
  return notes.filter(n =>
    n.title.toLowerCase().includes(q) || n.content.toLowerCase().includes(q)
  );
}

// ── const: editor ──
// Simplified editor using contentEditable
const editor = {
  init() {
    const el = document.getElementById('editorContent');
    el.innerHTML = '<p class="markdown">Start writing your first note here...</p>';
    el.setAttribute('contenteditable', 'true');
    el.setAttribute('spellcheck', 'false');
    el.focus();
    this.bindEvents(el);
  },

  bindEvents(el) {
    el.addEventListener('keydown', (e) => this.handleInput(e));
    el.addEventListener('input', () => this.save());
    el.addEventListener('click', (e) => this.handleClick(e));
  },

  handleInput(e) {
    if (e.key === 'Enter') {
      if (!e.shiftKey) {
        // Double enter creates new paragraph
        const selection = window.getSelection();
        const range = selection.getRangeAt(0);
        const parent = range.parentContainer;
        const before = range.startContainer;
        const after = range.endContainer;

        if (before === after) {
          const newDiv = document.createElement('div');
          newDiv.className = 'markdown';
          if (before && before.classList.contains('markdown')) {
            newDiv = document.createElement('div');
            newDiv.className = 'markdown';
          }
          if (before && before.classList.contains('markdown')) {
            before.parentNode.insertBefore(newDiv, after);
          }
          selection.setRange(newDiv, 0, 0);
          range.collapse();
        } else {
          const newLine = document.createElement('div');
          newLine.className = 'markdown';
          if (before && before.classList.contains('markdown')) {
            before.parentNode.insertBefore(newLine, after);
          }
          selection.setRange(newLine, 0, 0);
          range.collapse();
        }
      }
    }
  },

  handleClick(e) {
    const target = e.target;
    if (target.classList.contains('block')) {
      editor.activeBlock = target;
    }
  },

  save() {
    if (currentNoteId) {
      const el = document.getElementById('editorContent');
      const content = el.innerHTML.trim();
      updateNote(currentNoteId, { content });
    }
  },

  insertBlock(type) {
    const el = document.getElementById('editorContent');
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);
    const parent = range.parentContainer;

    if (type === 'paragraph') {
      const newDiv = document.createElement('div');
      newDiv.className = 'markdown';
      parent.insertBefore(newDiv, range.startContainer);
      range.setStart(newDiv, 0);
      range.collapse();
    } else if (type === 'list') {
      const newDiv = document.createElement('div');
      newDiv.className = 'markdown';
      parent.insertBefore(newDiv, range.startContainer);
      const li = document.createElement('li');
      li.textContent = '';
      newDiv.appendChild(li);
      range.setStart(li, 0);
      range.collapse();
    } else if (type === 'heading') {
      const newDiv = document.createElement('div');
      newDiv.className = 'markdown';
      parent.insertBefore(newDiv, range.startContainer);
      const h1 = document.createElement('h1');
      h1.textContent = 'New Heading';
      newDiv.appendChild(h1);
      range.setStart(h1, 0);
      range.collapse();
    } else if (type === 'image') {
      const newDiv = document.createElement('div');
      newDiv.className = 'markdown';
      parent.insertBefore(newDiv, range.startContainer);
      const img = document.createElement('img');
      img.src = 'https://via.placeholder.com/400x200?text=Image';
      img.alt = 'Image';
      newDiv.appendChild(img);
      range.setStart(img, 0);
      range.collapse();
    } else if (type === 'code') {
      const newDiv = document.createElement('div');
      newDiv.className = 'markdown';
      parent.insertBefore(newDiv, range.startContainer);
      const pre = document.createElement('pre');
      pre.className = 'editor-content code-block';
      const code = document.createElement('code');
      code.textContent = 'console.log("Hello, World!");';
      pre.appendChild(code);
      newDiv.appendChild(pre);
      range.setStart(code, 0);
      range.collapse();
    } else if (type === 'task') {
      const newDiv = document.createElement('div');
      newDiv.className = 'markdown';
      parent.insertBefore(newDiv, range.startContainer);
      const li = document.createElement('li');
      li.className = 'editor-content task';
      const cb = document.createElement('input');
      cb.type = 'checkbox';
      cb.className = 'task-checkbox';
      li.appendChild(cb);
      const span = document.createElement('span');
      span.textContent = '';
      li.appendChild(span);
      newDiv.appendChild(li);
      range.setStart(span, 0);
      range.collapse();
    } else if (type === 'table') {
      const newDiv = document.createElement('div');
      newDiv.className = 'markdown';
      parent.insertBefore(newDiv, range.startContainer);
      const table = document.createElement('table');
      table.className = 'editor-content';
      const tr = document.createElement('tr');
      const th1 = document.createElement('th');
      th1.textContent = 'Column 1';
      const th2 = document.createElement('th');
      th2.textContent = 'Column 2';
      tr.appendChild(th1);
      tr.appendChild(th2);
      table.appendChild(tr);
      newDiv.appendChild(table);
      range.setStart(th1, 0);
      range.collapse();
    }
  },

  insertHeading(level) {
    const el = document.getElementById('editorContent');
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);
    const parent = range.parentContainer;

    const newDiv = document.createElement('div');
    newDiv.className = 'markdown';
    parent.insertBefore(newDiv, range.startContainer);

    const h = document.createElement('h' + Math.min(Math.max(level, 1), 6));
    h.textContent = 'New Heading';
    newDiv.appendChild(h);
    range.setStart(h, 0);
    range.collapse();
  },

  insertImage() {
    const el = document.getElementById('editorContent');
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);
    const parent = range.parentContainer;

    const newDiv = document.createElement('div');
    newDiv.className = 'markdown';
    parent.insertBefore(newDiv, range.startContainer);

    const img = document.createElement('img');
    img.src = 'https://via.placeholder.com/400x200?text=Image';
    img.alt = 'Image';
    newDiv.appendChild(img);
    range.setStart(img, 0);
    range.collapse();
  },

  insertCode() {
    const el = document.getElementById('editorContent');
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);
    const parent = range.parentContainer;

    const newDiv = document.createElement('div');
    newDiv.className = 'markdown';
    parent.insertBefore(newDiv, range.startContainer);

    const pre = document.createElement('pre');
    pre.className = 'editor-content code-block';
    const code = document.createElement('code');
    code.textContent = 'console.log("Hello, World!");';
    pre.appendChild(code);
    newDiv.appendChild(pre);
    range.setStart(code, 0);
    range.collapse();
  },

  insertTask() {
    const el = document.getElementById('editorContent');
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);
    const parent = range.parentContainer;

    const newDiv = document.createElement('div');
    newDiv.className = 'markdown';
    parent.insertBefore(newDiv, range.startContainer);

    const li = document.createElement('li');
    li.className = 'editor-content task';
    const cb = document.createElement('input');
    cb.type = 'checkbox';
    cb.className = 'task-checkbox';
    li.appendChild(cb);
    const span = document.createElement('span');
    span.textContent = '';
    li.appendChild(span);
    newDiv.appendChild(li);
    range.setStart(span, 0);
    range.collapse();
  },

  insertTable() {
    const el = document.getElementById('editorContent');
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);
    const parent = range.parentContainer;

    const newDiv = document.createElement('div');
    newDiv.className = 'markdown';
    parent.insertBefore(newDiv, range.startContainer);

    const table = document.createElement('table');
    table.className = 'editor-content';
    const tr = document.createElement('tr');
    const th1 = document.createElement('th');
    th1.textContent = 'Column 1';
    const th2 = document.createElement('th');
    th2.textContent = 'Column 2';
    tr.appendChild(th1);
    tr.appendChild(th2);
    table.appendChild(tr);
    newDiv.appendChild(table);
    range.setStart(th1, 0);
    range.collapse();
  }
};

// ── const: ui ──
const ui = {
  // Sidebar
  showNotes() {
    const filtered = getSearchResults(document.getElementById('searchInput').value);
    const list = document.getElementById('noteList');
    const count = document.getElementById('sectionCount');

    list.innerHTML = '';
    filtered.forEach(note => {
      const li = document.createElement('li');
      li.className = 'note-item';
      li.dataset.noteId = note.id;
      li.innerHTML = `
        <div class="note-color" style="background:${note.color}"></div>
        <div class="note-info">
          <div class="note-title">${this.escapeHtml(note.title || 'Untitled')}</div>
          <div class="note-preview">${this.truncate(note.content, 50)}</div>
        </div>
        <div class="note-actions">
          ${note.isStarred ? '<svg class="note-star" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2.082m0 13.838A9 9 0 0122 11.918V22a2 2 0 11-4 0v-7.082c0-5.509-4.452-10.827-10-10.827S2.548 16.471 0 22v-7.082a9.003 9.003 0 0112-10.827z"/></svg>' : ''}
          <span class="note-date">${this.formatDate(note.updatedAt)}</span>
        </div>
      `;
      li.addEventListener('click', () => this.openNote(note));
      li.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        this.showDeleteModal(note);
      });
      list.appendChild(li);
    });

    count.textContent = `${filtered.length} ${filtered.length === 1 ? 'note' : 'notes'}`;
    updateStats();
  },

  openNote(note) {
    currentNoteId = note.id;
    const titleEl = document.getElementById('noteTitle');
    const contentEl = document.getElementById('editorContent');
    
    titleEl.textContent = note.title || 'Untitled';
    editor.init();
    
    // Set note title in the editor
    editor.content = note.content;
    editor.render();
  },

  showNewNoteModal() {
    document.getElementById('newNoteModal').classList.remove('hidden');
    document.getElementById('newNoteTitle').focus();
  },

  closeNewNoteModal() {
    document.getElementById('newNoteModal').classList.add('hidden');
    document.getElementById('newNoteTitle').value = '';
    document.getElementById('newNoteColor').value = '#6366f1';
    document.getElementById('newNoteDescription').value = '';
  },

  createNewNote() {
    const title = document.getElementById('newNoteTitle').value.trim();
    const color = document.getElementById('newNoteColor').value;
    const desc = document.getElementById('newNoteDescription').value.trim();
    
    const note = createNote({ title, color, content: desc || '' });
    this.closeNewNoteModal();
    this.openNote(note);
  },

  // Delete
  showDeleteModal(note) {
    document.getElementById('deleteModalMessage').textContent = `Delete "${this.escapeHtml(note.title || 'Untitled')}"?`;
    document.getElementById('deleteModal').classList.remove('hidden');
  },

  closeDeleteModal() {
    document.getElementById('deleteModal').classList.add('hidden');
  },

  deleteNote() {
    if (currentNoteId) {
      deleteNote(currentNoteId);
      this.closeDeleteModal();
      this.showNotes();
      if (currentNoteId === editingNote.id) {
        editor.init();
      }
    }
  },

  // Star
  toggleStar(note) {
    note.isStarred = !note.isStarred;
    updateNote(note.id, { isStarred: note.isStarred });
    this.showNotes();
  },

  // Stats
  updateStats() {
    const total = notes.length;
    const starred = notes.filter(n => n.isStarred).length;
    document.getElementById('statTotal').textContent = total;
    document.getElementById('statStarred').textContent = starred;
  },

  // Utilities
  formatDate(timestamp) {
    const date = new Date(timestamp);
    const options = { year: 'numeric', month: 'short', day: 'numeric' };
    return date.toLocaleDateString('en-US', options);
  },

  truncate(text, maxChars) {
    if (text.length <= maxChars) return text;
    return text.substring(0, maxChars - 1) + '…';
  },

  escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
  }
};

// ── const: wiring ──
const wiring = {
  init() {
    this.bindAll();
    this.initSidebar();
    this.initMain();
    this.initModals();
    this.initKeyboard();
    this.initResize();
  },

  bindAll() {
    // Sidebar
    document.getElementById('newNoteBtn').addEventListener('click', () => ui.showNewNoteModal());
    document.getElementById('showSidebarBtn').addEventListener('click', () => {
      const sidebar = document.getElementById('sidebar');
      sidebar.classList.toggle('collapsed');
    });
    document.getElementById('filterAll').addEventListener('click', () => {
      document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
      document.getElementById('filterAll').classList.add('active');
      ui.showNotes();
    });
    document.getElementById('filterStarred').addEventListener('click', () => {
      document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
      document.getElementById('filterStarred').classList.add('active');
      ui.showNotes();
    });
    document.getElementById('filterArchive').addEventListener('click', () => {
      document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
      document.getElementById('filterArchive').classList.add('active');
      ui.showNotes();
    });

    // Search
    document.getElementById('searchInput').addEventListener('input', () => {
      ui.showNotes();
    });

    // Main area
    document.getElementById('backBtn').addEventListener('click', () => {
      if (currentNoteId) {
        const note = getNote(currentNoteId);
        editor.init();
        editor.content = note.content;
        editor.render();
        document.getElementById('noteTitle').textContent = note.title || 'Untitled';
      }
    });
    document.getElementById('toggleStarBtn').addEventListener('click', () => {
      if (currentNoteId) {
        const note = getNote(currentNoteId);
        ui.toggleStar(note);
      }
    });
    document.getElementById('deleteNoteBtn').addEventListener('click', () => {
      if (currentNoteId) {
        ui.showDeleteModal(getNote(currentNoteId));
      }
    });

    // Editor content buttons
    document.getElementById('insertListBtn').addEventListener('click', () => editor.insertBlock('• '));
    document.getElementById('insertHeadingBtn').addEventListener('click', () => editor.insertBlock('# Note'));
    document.getElementById('insertImageBtn').addEventListener('click', () => editor.insertBlock('![image](https://via.placeholder.com/400x200?text=Image'));
    document.getElementById('insertCodeBtn').addEventListener('click', () => editor.insertBlock('```python\nconsole.log("Hello, World!");\n```'));
    document.getElementById('insertTaskBtn').addEventListener('click', () => editor.insertBlock('- Task Item'));
    document.getElementById('insertTableBtn').addEventListener('click', () => editor.insertBlock('| Column 1 | Column 2 |\n|---|---|\n| Data 1 | Data 2 |'));
  },

  initSidebar() {
    let resizeTimer;
    window.addEventListener('resize', () => {
      clearTimeout(resizeTimer);
      resizeTimer = setTimeout(() => {
        const sidebar = document.getElementById('sidebar');
        if (window.innerWidth < 768) {
          sidebar.classList.add('collapsed');
        }
      }, 100);
    });
  },

  initMain() {
    if (!currentNoteId) {
      const note = createNote({ title: 'Untitled Note', content: 'Start writing your first note here...' });
      currentNoteId = note.id;
      ui.openNote(note);
    } else {
      ui.showNotes();
    }
  },

  initModals() {
    document.getElementById('closeNewNoteModal').addEventListener('click', ui.closeNewNoteModal);
    document.getElementById('cancelNewNoteBtn').addEventListener('click', ui.closeNewNoteModal);
    document.getElementById('createNoteBtn').addEventListener('click', ui.createNewNote);
    
    document.getElementById('closeDeleteModal').addEventListener('click', ui.closeDeleteModal);
    document.getElementById('cancelDeleteBtn').addEventListener('click', ui.closeDeleteModal);
    document.getElementById('confirmDeleteBtn').addEventListener('click', ui.deleteNote);
  },

  initKeyboard() {
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') {
        document.querySelectorAll('.modal:not(.hidden)').forEach(m => m.classList.add('hidden'));
      }
      if ((e.ctrlKey || e.metaKey) && e.key === 'n') {
        e.preventDefault();
        ui.showNewNoteModal();
      }
      if ((e.ctrlKey || e.metaKey) && e.key === 's') {
        e.preventDefault();
        saveNotes();
        document.getElementById('footerStatus').textContent = 'Saved ✓';
        setTimeout(() => { document.getElementById('footerStatus').textContent = 'Ready'; }, 1500);
      }
    });
  },

  initResize() {
    window.addEventListener('resize', () => {
      if (window.innerWidth < 768) {
        document.getElementById('sidebar').classList.add('collapsed');
      }
    });
  }
};

// ── main ──
document.addEventListener('DOMContentLoaded', () => {
  loadNotes();
  wiring.init();
});
</script>
</body>
</html>