← Notes results

Notes

ornith 1.0 9b · middle

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 34,456 bytes · SHA-256 1785e8c3a6e6
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lumina Notes</title>
<style>
:root {
  --bg-primary: #0f0f13;
  --bg-secondary: #1a1a24;
  --bg-tertiary: #252538;
  --bg-hover: #2d2d44;
  --text-primary: #e8e8ef;
  --text-secondary: #9898b0;
  --text-muted: #6b6b85;
  --accent: #7c5cff;
  --accent-hover: #926dff;
  --border: rgba(255,255,255,0.06);
  --radius: 12px;
  --shadow: 0 4px 30px rgba(0,0,0,0.4);
  --transition: 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg-primary);
  color: var(--text-primary);
  overflow: hidden;
  height: 100vh;
}

/* Layout */
.app {
  display: grid;
  grid-template-columns: 260px 1fr;
  grid-template-rows: 48px 1fr;
  height: 100vh;
}

/* Top Bar */
.topbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  border-bottom: 1px solid var(--border);
  background: rgba(15, 15, 19, 0.85);
  backdrop-filter: blur(20px);
  z-index: 100;
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 16px;
}

.logo {
  font-size: 18px;
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.3px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.logo-icon {
  width: 28px;
  height: 28px;
  background: linear-gradient(135deg, var(--accent), #c77dff);
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.logo-icon svg { width: 16px; height: 16px; }

.search-input {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 7px 14px;
  color: var(--text-primary);
  font-size: 13px;
  width: 240px;
  transition: var(--transition);
}

.search-input:focus {
  outline: none;
  border-color: rgba(124,92,255,0.4);
  background: var(--bg-tertiary);
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.icon-btn {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: var(--transition);
}

.icon-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.topbar-spacer { flex: 1; }

/* Sidebar */
.sidebar {
  overflow-y: auto;
  padding: 16px 0;
  border-right: 1px solid var(--border);
  background: rgba(26, 26, 36, 0.5);
}

.sidebar::-webkit-scrollbar { width: 4px; }
.sidebar::-webkit-scrollbar-thumb { background: var(--bg-tertiary); border-radius: 4px; }

.sidebar-section { padding: 12px 0; }

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 20px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: var(--text-muted);
}

.section-header svg { opacity: 0.4; }

.section-header span.count {
  background: var(--bg-tertiary);
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  color: var(--text-muted);
}

.folder-list, .note-item {
  list-style: none;
}

.folder-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 20px;
  cursor: pointer;
  transition: var(--transition);
  user-select: none;
  color: var(--text-secondary);
}

.folder-item:hover { background: var(--bg-hover); color: var(--text-primary); }
.folder-item.active { background: rgba(124,92,255,0.12); color: var(--accent); border-left: 3px solid var(--accent); padding-left: 17px; }

.folder-icon { width: 20px; text-align: center; font-size: 16px; flex-shrink: 0; }
.folder-name { font-size: 14px; font-weight: 500; }

.note-item { margin-bottom: 1px; border-radius: 8px; overflow: hidden; }
.note-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 20px;
  cursor: pointer;
  transition: var(--transition);
}

.note-item:hover .note-header { background: rgba(255,255,255,0.03); }
.note-item.active .note-header { background: rgba(124,92,255,0.08); border-left: 3px solid var(--accent); padding-left: 17px; }

.note-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.dot-blue { background: #5b9fff; }
.dot-green { background: #4dc08f; }
.dot-yellow { background: #ffd166; }

.note-title-row { display: flex; align-items: baseline; gap: 8px; overflow: hidden; }
.note-name { 
  font-size: 13.5px; 
  font-weight: 500; 
  white-space: nowrap; 
  overflow: hidden; 
  text-overflow: ellipsis;
  color: var(--text-primary);
}

.note-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--text-muted);
  overflow: hidden;
  white-space: nowrap;
}

.note-excerpt {
  padding-left: 30px;
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Main Content */
.main {
  overflow-y: auto;
  background: var(--bg-primary);
}

.main::-webkit-scrollbar { width: 6px; }
.main::-webkit-scrollbar-thumb { background: var(--bg-tertiary); border-radius: 4px; }

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--text-muted);
}

.empty-icon { width: 64px; height: 64px; margin-bottom: 20px; opacity: 0.3; }
.empty-title { font-size: 18px; margin-bottom: 8px; color: var(--text-secondary); }
.empty-desc { font-size: 14px; max-width: 360px; text-align: center; line-height: 1.5; }

/* Editor */
.editor-wrapper { padding: 0; animation: fadeIn 200ms ease; }

.toolbar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 32px;
  border-bottom: 1px solid var(--border);
  background: rgba(26,26,36,0.3);
}

.toolbar-btn {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  transition: var(--transition);
}

.toolbar-btn:hover { background: var(--bg-hover); color: var(--text-primary); }

.editor-container { padding: 32px 48px 60px; max-width: 900px; }

.note-header-area { margin-bottom: 24px; }

.note-title-input {
  width: 100%;
  background: transparent;
  border: none;
  color: var(--text-primary);
  font-size: 36px;
  font-weight: 700;
  padding: 8px 0;
  outline: none;
  letter-spacing: -1.5px;
  line-height: 1.2;
}

.note-title-input::placeholder { color: var(--text-muted); }

.note-date-display {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 8px;
}

.editor-area { outline: none; min-height: 400px; line-height: 1.75; }

/* Rich text styling */
.editor-area h1 { font-size: 22px; font-weight: 700; margin: 16px 0 8px; letter-spacing: -0.3px; color: var(--text-primary); }
.editor-area h2 { font-size: 18px; font-weight: 600; margin: 14px 0 6px; letter-spacing: -0.2px; color: var(--text-primary); }
.editor-area h3 { font-size: 15px; font-weight: 600; margin: 12px 0 4px; letter-spacing: -0.1px; color: var(--text-primary); }
.editor-area p { margin-bottom: 8px; color: var(--text-primary); }
.editor-area ul, .editor-area ol { padding-left: 24px; margin-bottom: 8px; }
.editor-area li { margin-bottom: 3px; }
.editor-area blockquote { border-left: 3px solid var(--accent); padding-left: 16px; color: var(--text-secondary); margin: 12px 0; font-style: italic; }
.editor-area code { background: var(--bg-tertiary); padding: 2px 7px; border-radius: 4px; font-size: 13px; color: #a5c9ff; font-family: 'JetBrains Mono', monospace; }
.editor-area pre { background: var(--bg-secondary); padding: 16px; border-radius: 8px; overflow-x: auto; margin-bottom: 12px; }
.editor-area pre code { background: none; padding: 0; }
.editor-area hr { border: none; border-top: 1px solid var(--border); margin: 16px 0; }

/* Preview mode */
.preview-mode .editor-area { outline: none; min-height: 400px; line-height: 1.75; }
.preview-mode .toolbar { display: none; }

/* Modal */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.6);
  backdrop-filter: blur(4px);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  animation: fadeIn 150ms ease forwards;
}

.modal {
  background: var(--bg-secondary);
  border-radius: 16px;
  padding: 32px;
  width: 480px;
  max-width: 90vw;
  box-shadow: var(--shadow);
  transform: translateY(10px);
  animation: slideUp 200ms ease forwards;
}

.modal-title { font-size: 16px; font-weight: 600; margin-bottom: 20px; color: var(--text-primary); }

.modal-input {
  width: 100%;
  background: var(--bg-tertiary);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 14px;
  color: var(--text-primary);
  font-size: 13.5px;
  outline: none;
  transition: var(--transition);
  margin-bottom: 12px;
}

.modal-input:focus { border-color: rgba(124,92,255,0.4); }
.modal-input::placeholder { color: var(--text-muted); }

.modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 20px; }

.btn-primary {
  background: var(--accent);
  color: white;
  border: none;
  padding: 9px 20px;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 500;
  cursor: pointer;
  transition: var(--transition);
}

.btn-primary:hover { background: var(--accent-hover); }

.btn-secondary {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  padding: 9px 20px;
  border-radius: 8px;
  font-size: 13.5px;
  cursor: pointer;
  transition: var(--transition);
}

.btn-secondary:hover { background: var(--bg-hover); color: var(--text-primary); }

/* Tags */
.tags-container { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }

.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  background: rgba(124,92,255,0.15);
  border-radius: 6px;
  font-size: 12px;
  color: var(--accent);
}

.tag-chip .tag-dot { width: 5px; height: 5px; background: currentColor; border-radius: 50%; }

/* Animations */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

/* Toast */
.toast-container { position: fixed; bottom: 24px; right: 24px; z-index: 2000; display: flex; flex-direction: column; gap: 8px; }

.toast {
  background: var(--bg-tertiary);
  border: 1px solid var(--border);
  padding: 12px 18px;
  border-radius: 10px;
  font-size: 13.5px;
  color: var(--text-primary);
  box-shadow: var(--shadow);
  animation: toastIn 250ms ease, toastOut 200ms ease 2.8s forwards;
}

@keyframes toastIn { from { transform: translateX(40px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@keyframes toastOut { from { opacity: 1; } to { opacity: 0; } }

/* Context menu */
.context-menu {
  position: fixed;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 6px;
  box-shadow: var(--shadow);
  z-index: 999;
  min-width: 180px;
}

.context-menu-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13.5px;
  color: var(--text-secondary);
  transition: var(--transition);
}

.context-menu-item:hover { background: var(--bg-hover); color: var(--text-primary); }

/* Responsive */
@media (max-width: 768px) {
  .app { grid-template-columns: 1fr; grid-template-rows: 48px auto 1fr; }
  .sidebar { display: none; }
  .editor-container { padding: 20px 16px 40px; }
}

/* Selection */
::selection { background: rgba(124,92,255,0.3); }
</style>
</head>
<body>
<div class="app">
  <!-- Top Bar -->
  <div class="topbar">
    <div class="topbar-left">
      <div class="logo">
        <div class="logo-icon">
          <svg viewBox="0 0 24 24" fill="none"><path d="M3.5 18.5L12 6l8.5 12.5" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
        </div>
        Lumina
      </div>
      <input class="search-input" type="text" placeholder="Search notes..." id="searchInput">
    </div>
    <div class="topbar-right">
      <button class="icon-btn" onclick="showModal('settings')" title="Settings">⚙</button>
      <button class="icon-btn" onclick="togglePreview()" title="Toggle Preview" id="previewBtn">◀▶</button>
    </div>
  </div>

  <!-- Sidebar -->
  <div class="sidebar" id="sidebar">
    <ul class="folder-list" id="folders">
      <li class="folder-item active" data-id="inbox">
        <span class="folder-icon">📥</span><span class="folder-name">Inbox</span>
        <span class="count" id="folder-count-inbox">0</span>
      </li>
      <li class="folder-item" data-id="personal">
        <span class="folder-icon">👤</span><span class="folder-name">Personal</span>
        <span class="count" id="folder-count-personal">0</span>
      </li>
      <li class="folder-item" data-id="work">
        <span class="folder-icon">💼</span><span class="folder-name">Work</span>
        <span class="count" id="folder-count-work">0</span>
      </li>
    </ul>

    <div class="sidebar-section">
      <div class="section-header">
        All Notes
        <span style="font-size:16px;">+</span>
      </div>
      <ul class="folder-list" id="noteList"></ul>
    </div>
  </div>

  <!-- Main Content -->
  <div class="main" id="main">
    <div class="empty-state" id="emptyState">
      <svg class="empty-icon" viewBox="0 0 24 24" fill="none"><path d="M12 3L5.5 9H18.5L12 3ZM5.5 9L9 14L6.5 20L3.5 17L6.5 12L5.5 9ZM12 9L18.5 3L21.5 6L18 10L21.5 14L20 16.5L16.5 12L12 16.5L7.5 12L4 14.5L5.5 16.5L9 12L12 9Z" stroke="currentColor" stroke-width="1.2" fill="none"/></svg>
      <span class="empty-title">No notes selected</span>
      <span class="empty-desc">Click a note from the sidebar or create a new one to get started.</span>
    </div>

    <div class="editor-wrapper" id="editorWrapper" style="display:none;">
      <div class="toolbar">
        <button class="toolbar-btn" onclick="format('bold')" title="Bold (Cmd+B)">B</button>
        <button class="toolbar-btn" onclick="format('italic')" title="Italic (Cmd+I)">I</button>
        <button class="toolbar-btn" onclick="format('underline')" title="Underline (Cmd+U)">U</button>
        <button class="toolbar-btn" onclick="format('strikethrough')" title="Strikethrough">S</button>
        <div style="width:1px;height:20px;background:var(--border);"></div>
        <button class="toolbar-btn" onclick="format('bulletList')" title="Bullet List">•</button>
        <button class="toolbar-btn" onclick="format('orderedList')" title="Ordered List">1.</button>
        <button class="toolbar-btn" onclick="format('blockquote')" title="Quote">"</button>
        <div style="width:1px;height:20px;background:var(--border);"></div>
        <button class="toolbar-btn" onclick="format('codeBlock')" title="Code Block">&lt;/&gt;</button>
        <button class="toolbar-btn" onclick="format('link')" title="Link">🔗</button>
      </div>

      <div class="editor-container">
        <div class="note-header-area">
          <input class="note-title-input" id="titleInput" placeholder="Untitled Note" maxlength="200"/>
          <div class="note-date-display" id="dateDisplay"></div>
        </div>

        <textarea class="editor-area" id="editorArea" spellcheck="false" ondrop="dropFile(event)" ontouchstart=""></textarea>

        <div style="margin-top: 20px; display:flex; justify-content:space-between;">
          <div style="display:flex; gap:8px;">
            <input class="modal-input" id="tagInput" placeholder="Add tag..." onkeydown="handleTagKey(event)" style="max-width:180px;" />
            <span id="tagsDisplay"></span>
          </div>
          <div style="display:flex; gap:8px;">
            <button class="icon-btn" onclick="deleteNote()" title="Delete (Del)">🗑</button>
            <button class="btn-secondary" onclick="saveNote()">💾 Save</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- Modal -->
<div class="modal-overlay" id="modalOverlay" style="display:none;" onclick="closeModal(event)">
  <div class="modal">
    <div class="modal-title" id="modalTitle"></div>
    <input class="modal-input" id="modalInput" placeholder="" onkeydown="if(event.key==='Enter')saveFromModal()"/>
    <div class="modal-actions" id="modalActions"></div>
  </div>
</div>

<!-- Context Menu -->
<div class="context-menu" id="contextMenu" style="display:none;" onclick="closeContextMenu()">
  <div class="context-menu-item" onclick="createNoteFromContext()">📝 Create Note Here</div>
  <div class="context-menu-item" onclick="copyLink('note')">🔗 Copy Link</div>
  <div class="context-menu-item" style="color:var(--text-muted);" onclick="closeContextMenu()">Close</div>
</div>

<!-- Toast Container -->
<div class="toast-container" id="toasts"></div>

<script>
// State
let notes = JSON.parse(localStorage.getItem('lumina-notes') || '[]');
let activeNoteId = null;
let currentFolder = 'inbox';

const FOLDER_ICONS = { inbox: '📥', personal: '👤', work: '💼' };
const FOLDER_COLORS = { inbox: 'dot-blue', personal: 'dot-green', work: 'dot-yellow' };
const FOLDER_LABELS = { inbox: 'Inbox (Default)', personal: 'Personal', work: 'Work' };

// Init
function init() {
  if (!notes.length) seedSampleNotes();
  renderFolders();
  renderNoteList();
}

function seedSampleNotes() {
  const now = () => new Date().toISOString().split('T')[0];
  notes = [
    { id: 'n1', folder: 'inbox', title: 'Project Brainstorm Ideas', date: now(), content: '<p>Here are some ideas I want to explore this quarter:</p><ul><li>New mobile app for team collaboration</li><li>AI-powered analytics dashboard</li><li>Open-source design system toolkit</li></ul><blockquote>Remember: focus on user value first, technology second.</blockquote>', tags: ['ideas', '2025Q1'], starred: true },
    { id: 'n2', folder: 'work', title: 'Sprint Planning Notes', date: now(), content: '<h2>Sprint Goals</h2><p>Complete authentication module and API integration.</p><ol><li>Set up Auth0 configuration</li><li>Implement OAuth flow</li><li>Create user profile endpoints</li></ol>', tags: ['work'], starred: false },
    { id: 'n3', folder: 'personal', title: 'Book Recommendations 2025', date: now(), content: '<h2>This quarter I want to read:</h2><p>1. <em>The Creative Act</em> - Rick Rubin<br/>2. <em>Devil in the Detail</em> by Peter Sims<br/>3. Something on behavioral design patterns', tags: ['reading'], starred: true },
    { id: 'n4', folder: 'inbox', title: 'Meeting Notes — Design Review', date: now(), content: '<p><strong>Date:</strong> Today</p><h2>Action Items</h2><ul><li>Rewire navigation component to new design token system</li><li>Update accessibility audit report</li></ul>', tags: ['meetings'], starred: false },
    { id: 'n5', folder: 'work', title: 'Architecture Decision Record — API Gateway', date: now(), content: '<h2>Context</h2><p>We need to route all external traffic through a central gateway for monitoring and rate limiting.</p><h3>Proposed Solution</h3><pre><code class="language-json">{\n  "gateway": "kong\",\n  "rate_limit": 100,\n  "cors_enabled": true\n}</code></pre>', tags: ['architecture', 'api'], starred: false },
    { id: 'n6', folder: 'personal', title: 'Morning Routine — Draft', date: now(), content: '<ul><li>Wake up at 6:30</li><li>Meditation (10 min)</li><li>Morning pages / journaling (15 min)</li><li>Exercise session (45 min)</li></ul>', tags: ['habits'], starred: true },
    { id: 'n7', folder: 'inbox', title: 'Random Thoughts & Inspiration', date: now(), content: '<p>"The best way to predict the future is to create it."</p><hr/><p>Notes about upcoming events and reminders floating here...</p>', tags: [], starred: false },
    { id: 'n8', folder: 'work', title: 'Q1 OKRs Draft', date: now(), content: '<h2>Objectives & Key Results</h2><h3>O1: Improve platform performance</h3><p>KR1: Reduce page load time by 40%</p><p>KR2: Achieve 99.5% uptime SLA</p>', tags: ['okr', 'goals'], starred: false },
    { id: 'n9', folder: 'inbox', title: 'Recipe Collection — Try This Week', date: now(), content: '<h2>This week:</h2><ol><li>Pasta aglio e olio with chili flakes</li><li>Avocado toast variations</li></ol>', tags: ['recipes'], starred: false },
    { id: 'n10', folder: 'personal', title: 'Travel Wishlist 2025-2026', date: now(), content: '<ul><li>🇯🇵 Tokyo — Spring (sakura season)</li><li>🇮🇹 Florence — Autumn</li><li>🇨🇳 Shanghai — Winter</li></ul>', tags: ['travel'], starred: true },
  ];
  saveNotes();
}

function saveNotes() {
  localStorage.setItem('lumina-notes', JSON.stringify(notes));
}

// Folder switching
function renderFolders() {
  for (const folder of Object.keys(FOLDER_ICONS)) {
    const el = document.querySelector(`.folder-item[data-id="${folder}"]`);
    if (!el) continue;
    const count = notes.filter(n => n.folder === folder).length;
    document.getElementById(`folder-count-${folder}`).textContent = count;
  }

  for (const folder of Object.keys(FOLDER_ICONS)) {
    const el = document.querySelector(`.folder-item[data-id="${folder}"]`);
    if (!el) continue;
    el.addEventListener('click', () => switchFolder(folder));
  }
}

function switchFolder(folder) {
  currentFolder = folder;
  renderNoteList();
  // Update active state
  document.querySelectorAll('.folder-item').forEach(el => el.classList.toggle('active', el.dataset.id === folder));
  if (currentFolder !== 'all' && !notes.find(n => n.id === activeNoteId)) {
    showNote(activeNoteId);
  }
}

function renderNoteList() {
  const container = document.getElementById('noteList');
  let filteredNotes;
  const query = document.getElementById('searchInput').value.toLowerCase().trim();

  if (currentFolder === 'all') {
    filteredNotes = notes.slice();
  } else {
    filteredNotes = notes.filter(n => n.folder === currentFolder);
  }

  // Sort by date desc, starred first
  filteredNotes.sort((a, b) => {
    if (a.starred !== b.starred) return a.starred ? -1 : 1;
    return new Date(b.date) - new Date(a.date);
  });

  container.innerHTML = filteredNotes.map(n => `
    <li class="note-item ${n.id === activeNoteId ? 'active' : ''}" data-id="${n.id}">
      <div class="note-header" onclick="showNote('${n.id}')">
        <span class="note-dot ${FOLDER_COLORS[n.folder]}"></span>
        <div class="note-title-row">
          <span class="note-name">${esc(n.title)}</span>
        </div>
      </div>
      <div class="note-meta" onclick="event.stopPropagation(); toggleStar('${n.id}')">
        ${n.starred ? '⭐' : '☆'}
        <span>${shortDate(n.date)}</span>
        ${n.tags.length ? n.tags.join(' · ') : ''}
      </div>
      <div class="note-excerpt">${esc(n.content).replace(/<[^>]+>/g, '').slice(0, 140) + (notes.find(x => x.id === n.id)?.content?.length > 150 ? '...' : '')}</div>
    </li>
  `).join('');

  container.querySelectorAll('.note-item').forEach(el => {
    el.addEventListener('click', () => showNote(el.dataset.id));
  });
}

// Note display
function showNote(id) {
  activeNoteId = id;
  const note = notes.find(n => n.id === id);
  if (!note) return;

  document.getElementById('emptyState').style.display = 'none';
  const wrapper = document.getElementById('editorWrapper');
  wrapper.style.display = '';

  document.getElementById('titleInput').value = note.title || '';
  document.getElementById('editorArea').value = note.content || '';
  document.getElementById('dateDisplay').textContent = shortDate(note.date);
  updateTagsDisplay();

  // Update active state in list
  renderNoteList();
}

function deleteNote() {
  if (!activeNoteId) return;
  const idx = notes.findIndex(n => n.id === activeNoteId);
  if (idx === -1) return;
  const removed = notes.splice(idx, 1)[0];
  saveNotes();
  renderFolders();
  renderNoteList();

  showToast(`${removed.title} deleted`, '2000');
  if (activeNoteId !== null) {
    activeNoteId = null;
    document.getElementById('emptyState').style.display = '';
    document.getElementById('editorWrapper').style.display = 'none';
  }
}

function toggleStar(id) {
  const note = notes.find(n => n.id === id);
  if (!note) return;
  note.starred = !note.starred;
  saveNotes();
  renderNoteList();
  if (activeNoteId === id) updateTagsDisplay();
}

// Editor
function format(cmd, value = null) {
  document.execCommand(cmd, false, value);
  const area = document.getElementById('editorArea');
  area.focus();
}

function saveNote() {
  const title = document.getElementById('titleInput').value.trim();
  let content = document.getElementById('editorArea').value;
  // Basic cleanup: wrap loose <p> that aren't inside list/blockquote/code in proper tags
  if (!content.trim()) return showToast('Nothing to save', '1500');

  const idx = notes.findIndex(n => n.id === activeNoteId);
  let note = notes[idx];
  if (!note) {
    note = createNote({ title, content });
  } else {
    note.title = title;
    note.content = content;
    saveNotes();
    renderFolders();
  }

  // Extract tags from content if any
  const tagMatch = content.match(/#(\w+)/g);
  if (tagMatch) {
    note.tags = [...new Set(tagMatch.map(m => m.slice(1)))];
    document.getElementById('tagsDisplay').innerHTML = note.tags.map(t => `<span class="tag-chip"><span class="tag-dot"></span>${esc(t)}</span>`).join('');
  }

  saveNotes();
  renderNoteList();
}

// Tag handling
function updateTagsDisplay() {
  const note = notes.find(n => n.id === activeNoteId);
  if (!note) return;
  document.getElementById('tagsDisplay').innerHTML = note.tags.map(t => `<span class="tag-chip"><span class="tag-dot"></span>${esc(t)}</span>`).join('');
}

function handleTagKey(e) {
  const input = e.target;
  if (e.key === 'Enter' || e.key === ',') {
    e.preventDefault();
    const val = input.value.trim();
    if (!val) return;
    saveNoteFromTagsInput();
    updateTagsDisplay();
  } else if (e.key === 'Backspace' && !input.value) {
    // remove last tag
    let note = notes.find(n => n.id === activeNoteId);
    if (!note) return;
    const idx = note.tags.lastIndexOf(input.previousValue || '');
    if (idx !== -1) note.tags.splice(idx, 1);
    input.previousValue = null;
    saveNotes();
    updateTagsDisplay();
  }
}

function addTag(val) {
  let note = notes.find(n => n.id === activeNoteId);
  if (!note) return;
  note.tags = [...new Set([val, ...note.tags])];
  note.content += `\n#${val}`;
  saveNotes();
  renderNoteList();
  updateTagsDisplay();
}

function removeTag(val) {
  let note = notes.find(n => n.id === activeNoteId);
  if (!note) return;
  // Update content to remove #tag line
  const lines = note.content.split('\n');
  for (let i = 0; i < lines.length; i++) {
    if (lines[i].startsWith(`#${val}`)) {
      lines.splice(i, 1);
      break;
    }
  }
  note.content = lines.join('\n').trim() + '\n';
  note.tags = note.tags.filter(t => t !== val);
  saveNotes();
  renderNoteList();
}

function saveNoteFromTagsInput() {
  const tag = document.getElementById('tagInput').value.trim();
  if (!tag) return;
  addTag(tag);
}

// Context menu
let contextTargetId = null;
document.addEventListener('contextmenu', (e) => {
  e.preventDefault();
  // Don't show if clicking on an element
  if (e.target.closest('.note-item') || e.target.closest('#editorWrapper')) return;
  contextTargetId = e.clientY + ',' + e.clientX;
  const rect = document.querySelector('.main').getBoundingClientRect();
  const x = Math.min(e.clientX, rect.width - 180);
  const y = e.clientY - rect.top;
  document.getElementById('contextMenu').style.left = x + 'px';
  document.getElementById('contextMenu').style.top = (y < 24 ? 24 : y) + 'px';
  document.getElementById('contextMenu').style.display = '';
});

function closeContextMenu() {
  document.getElementById('contextMenu').style.display = 'none';
}

document.addEventListener('click', () => closeContextMenu());

function createNoteFromContext() {
  const x = contextTargetId.split(',');
  const y = parseInt(x[1]) + 40;
  const rect = document.querySelector('.main').getBoundingClientRect();
  // Insert after editor wrapper
  if (activeNoteId) return showToast('Cannot insert here', '1500');

  const titleInput = document.getElementById('titleInput');
  const textarea = document.getElementById('editorArea');
  const dateDisplay = document.getElementById('dateDisplay');

  activeNoteId = 'ctx-' + Date.now();
  notes.unshift({ id: activeNoteId, folder: currentFolder || 'inbox', title: '', content: '<p>Double-click to edit...</p>', tags: [], starred: false, date: new Date().toISOString() });

  const editorContainer = document.getElementById('editorWrapper');
  // Move all children before the empty state
  while (document.getElementById('emptyState').children.length) {
    editorContainer.insertBefore(document.getElementById('emptyState').firstChild, document.getElementById('emptyState'));
  }

  titleInput.focus();
  setTimeout(() => textarea.click(), 50);
}

function copyLink(type) {
  const url = type === 'note' ? (activeNoteId ? '#/notes/' + activeNoteId : '') : '';
  // In a real app this would use navigator.clipboard; just show toast for prototype
  showToast('Link copied to clipboard', '2000');
}

// Modal
function showModal(title) {
  document.getElementById('modalOverlay').style.display = 'flex';
  const modalTitle = document.getElementById('modalTitle');
  if (title === 'settings') {
    modalTitle.textContent = 'Settings';
    document.getElementById('modalInput').value = '';
    document.getElementById('modalActions').innerHTML = `
      <button class="btn-secondary" onclick="showModal()">Cancel</button>
      <button class="btn-primary" onclick="closeModal(event)">Save</button>
    `;
  } else {
    modalTitle.textContent = title;
    document.getElementById('modalInput').value = '';
    document.getElementById('modalActions').innerHTML = `
      <button class="btn-secondary" onclick="showModal()">Cancel</button>
      <button class="btn-primary" onclick="saveFromModal(); closeModal(event)">Create</button>
    `;
  }
}

function closeModal(e) {
  if (e && e.target !== document.getElementById('modalOverlay')) return;
  document.getElementById('modalOverlay').style.display = 'none';
}

function saveFromModal() {
  const input = document.getElementById('modalInput');
  const val = input.value.trim();
  if (!val) return;

  // Create new note in current folder
  addNote(val, currentFolder);

  closeModal();
}

function addNote(title, folder) {
  const now = new Date().toISOString();
  notes.unshift({ id: 'new-' + Date.now(), folder: folder || 'inbox', title, content: '<p>Start writing...</p>', tags: [], starred: false, date: now });
  saveNotes();
  renderFolders();
}

// Preview toggle
let isPreview = false;
function togglePreview() {
  isPreview = !isPreview;
  document.getElementById('editorWrapper').classList.toggle('preview-mode', isPreview);
  const btn = document.getElementById('previewBtn');
  btn.textContent = isPreview ? '✏️' : '◀▶';
}

// Utilities
function esc(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
function shortDate(iso) { const d = new Date(iso); return `${d.getMonth()+1}/${d.getDate()}`; }

function showToast(msg, duration = 2000) {
  const container = document.getElementById('toasts');
  const toast = document.createElement('div');
  toast.className = 'toast';
  toast.textContent = msg;
  container.appendChild(toast);
  setTimeout(() => toast.remove(), duration + 350);
}

// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
  if ((e.ctrlKey || e.metaKey) && e.key === 'b') { format('bold'); e.preventDefault(); }
  if ((e.ctrlKey || e.metaKey) && e.key === 'i') { format('italic'); e.preventDefault(); }
  if ((e.ctrlKey || e.metaKey) && e.key === 'u') { format('underline'); e.preventDefault(); }

  // Ctrl/Cmd + Enter to save
  if ((e.ctrlKey || e.metaKey) && e.key === 'Enter' && !e.target.isContentEditable && document.activeElement.tagName !== 'INPUT' && document.activeElement.id !== 'searchInput') {
    e.preventDefault();
    saveNote();
  }

  // Delete to close context menu
  if (e.key === 'Escape') closeContextMenu();

  // Ctrl/Cmd + Shift + N for new note modal
  if ((e.ctrlKey || e.metaKey) && !e.shiftKey && e.key.toLowerCase() === 'n' && document.activeElement.id !== 'searchInput') {
    e.preventDefault();
    showModal('New Note');
  }

  // Ctrl/Cmd + O to search
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'o' && !document.querySelector('.modal-overlay').style.display || document.getElementById('modalOverlay').style.display !== 'flex') {
    const input = document.getElementById('searchInput');
    if (input) { input.focus(); input.select(); }
  }

  // Ctrl/Cmd + Backspace to delete note
  if ((e.ctrlKey || e.metaKey) && !e.shiftKey && e.key === 'Backspace' && activeNoteId) {
    e.preventDefault();
    deleteNote();
  }
});

// Search filter
document.getElementById('searchInput').addEventListener('input', () => renderNoteList());

// File drop
function dropFile(e) {
  e.preventDefault();
  const files = e.dataTransfer.files;
  if (files.length === 1 && files[0].name.endsWith('.md')) {
    const note = notes.find(n => n.id === activeNoteId);
    if (!note) return showToast('No note open to import', '1500');
    note.content += `\n\n---\nImported from ${files[0].name}\n`;
    saveNotes();
    renderNoteList();
    updateTagsDisplay();
  } else if (files.length > 0) {
    // Just read text content as notes
    for (const file of files) {
      const reader = new FileReader();
      reader.onload = () => {
        let note = notes.find(n => n.id === activeNoteId);
        if (!note) return;
        note.content += `\n\n---\nImported from ${file.name}\n${reader.result.slice(0, 500)}`;
        saveNotes();
        renderNoteList();
      };
      reader.readAsText(file);
    }
  } else {
    showToast('No file to import', '1200');
  }
}

// Init everything
init();
</script>
</body>
</html>
<!-- agent-meta {"model":"ornith-1.0-9b","provider":"lmstudio","persona":"middle","sessionId":"9c9ec086-1f71-4865-8bee-f4b065857e3f","tokensIn":25829,"tokensOut":11539,"tokensTotal":37368,"turns":3,"toolCalls":2,"failedToolCalls":0,"timestamp":"2026-07-13T18:20:16.844Z"} -->