← Notes results

Notes

inkling · basic

Initial view of Notes

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 26,403 bytes · SHA-256 a041cd42a5b1
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Vault — Notes</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">

  <style>
    :root {
      --bg: #0b0e14;
      --surface: #11131a;
      --surface-2: #161923;
      --surface-3: #1a1d28;
      --border: rgba(255,255,255,0.06);
      --border-hover: rgba(255,255,255,0.12);
      --text: #e8e8ec;
      --text-dim: #8a8f9d;
      --text-muted: #4a4f5e;
      --accent: #7c8cff;
      --accent-soft: rgba(124,140,255,0.12);
      --accent-glow: rgba(124,140,255,0.25);
      --danger: #ff6b6b;
      --success: #6bffb8;
      --shadow: rgba(0,0,0,0.45);
    }

    * { box-sizing: border-box; margin: 0; padding: 0; }
    html, body { height: 100%; overflow: hidden; }

    body {
      background: var(--bg);
      color: var(--text);
      font-family: 'Inter', system-ui, sans-serif;
      font-size: 15px;
      line-height: 1.6;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }

    ::selection { background: var(--accent-soft); color: var(--text); }

    /* Layout */
    .app {
      display: flex;
      height: 100vh;
      width: 100vw;
      overflow: hidden;
    }

    /* Sidebar */
    .sidebar {
      width: 320px;
      min-width: 260px;
      background: linear-gradient(180deg, #0f1118 0%, #0b0e14 100%);
      border-right: 1px solid var(--border);
      display: flex;
      flex-direction: column;
      position: relative;
      box-shadow: 8px 0 30px rgba(0,0,0,0.3);
      z-index: 10;
    }

    .sidebar .brand {
      padding: 28px 24px 18px;
      display: flex;
      align-items: center;
      gap: 14px;
      border-bottom: 1px solid var(--border);
    }
    .brand-icon {
      width: 36px; height: 36px;
      background: linear-gradient(135deg, #6b7bff, #a6b0ff);
      border-radius: 10px;
      display: grid;
      place-items: center;
      box-shadow: 0 0 20px var(--accent-glow);
    }
    .brand-icon i { color: #fff; font-size: 15px; }
    .brand h1 {
      font-family: 'Instrument Serif', Georgia, serif;
      font-size: 22px;
      font-weight: 400;
      letter-spacing: -0.02em;
      color: #fff;
    }
    .brand span {
      display: block;
      font-family: 'Inter', sans-serif;
      font-size: 10px;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--text-muted);
      font-weight: 600;
      margin-top: 2px;
    }

    .search-box {
      padding: 18px 20px 10px;
      position: relative;
    }
    .search-box input {
      width: 100%;
      padding: 10px 12px 10px 36px;
      background: var(--surface-2);
      border: 1px solid var(--border);
      border-radius: 10px;
      color: var(--text);
      font-size: 13px;
      transition: all 0.2s ease;
    }
    .search-box input:focus {
      outline: none;
      border-color: var(--accent);
      box-shadow: 0 0 0 3px var(--accent-soft);
    }
    .search-box i {
      position: absolute;
      left: 32px;
      top: 28px;
      color: var(--text-muted);
      font-size: 12px;
      pointer-events: none;
    }

    .folders {
      padding: 8px 16px 4px;
    }
    .folder-label {
      font-size: 10px;
      text-transform: uppercase;
      letter-spacing: 0.1em;
      color: var(--text-muted);
      font-weight: 600;
      margin-bottom: 6px;
      padding-left: 4px;
    }

    /* Note list */
    .note-list {
      flex: 1;
      overflow-y: auto;
      padding: 4px 16px 24px;
    }
    .note-list::-webkit-scrollbar { width: 6px; }
    .note-list::-webkit-scrollbar-track { background: transparent; }
    .note-list::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 10px; }
    .note-list::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

    .note-card {
      padding: 14px 14px;
      border-radius: 12px;
      border: 1px solid transparent;
      cursor: pointer;
      transition: all 0.15s ease;
      margin-bottom: 6px;
      position: relative;
      background: transparent;
    }
    .note-card:hover {
      background: rgba(255,255,255,0.03);
      border-color: var(--border-hover);
    }
    .note-card.active {
      background: var(--surface-2);
      border-color: var(--border-hover);
      box-shadow: 0 4px 20px rgba(0,0,0,0.25);
    }
    .note-card.active .note-title { color: #fff; }
    .note-card .note-title {
      font-weight: 600;
      font-size: 14px;
      color: var(--text);
      margin-bottom: 6px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .note-card .note-preview {
      font-size: 12px;
      color: var(--text-dim);
      line-height: 1.5;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
      font-family: 'Inter', sans-serif;
    }
    .note-card .note-meta {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-top: 10px;
      font-size: 11px;
      color: var(--text-muted);
    }
    .note-card .tags {
      display: flex;
      gap: 4px;
      flex-wrap: wrap;
      margin-top: 8px;
    }
    .tag {
      font-size: 10px;
      padding: 2px 6px;
      border-radius: 4px;
      background: var(--surface-3);
      color: var(--text-dim);
      border: 1px solid var(--border);
    }

    /* Main area */
    .main {
      flex: 1;
      display: flex;
      flex-direction: column;
      background: radial-gradient(ellipse at 50% 0%, #141620 0%, #0b0e14 70%);
      position: relative;
    }

    /* Top toolbar */
    .toolbar {
      height: 68px;
      border-bottom: 1px solid var(--border);
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 32px;
      background: rgba(11,14,20,0.6);
      backdrop-filter: blur(12px) saturate(120%);
      -webkit-backdrop-filter: blur(12px) saturate(120%);
      z-index: 5;
    }
    .toolbar-left { display: flex; align-items: center; gap: 12px; }
    .toolbar h2 {
      font-family: 'Instrument Serif', Georgia, serif;
      font-size: 26px;
      font-weight: 400;
      letter-spacing: -0.03em;
      color: #fff;
      line-height: 1;
    }
    .toolbar-sub {
      font-size: 12px;
      color: var(--text-muted);
      margin-top: 4px;
    }
    .toolbar-right { display: flex; gap: 10px; align-items: center; }

    .btn {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 8px 16px;
      border-radius: 10px;
      border: 1px solid var(--border);
      background: var(--surface);
      color: var(--text-dim);
      font-family: 'Inter', sans-serif;
      font-size: 13px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s ease;
    }
    .btn:hover {
      border-color: var(--border-hover);
      color: var(--text);
      background: var(--surface-2);
    }
    .btn-primary {
      background: linear-gradient(135deg, #6b7bff, #8c9eff);
      color: #fff;
      border-color: transparent;
      box-shadow: 0 4px 15px rgba(124,140,255,0.25);
    }
    .btn-primary:hover {
      box-shadow: 0 6px 25px rgba(124,140,255,0.35);
      transform: translateY(-1px);
    }
    .btn-danger {
      color: var(--danger);
      border-color: rgba(255,107,107,0.2);
      background: rgba(255,107,107,0.05);
    }
    .btn-danger:hover {
      background: rgba(255,107,107,0.1);
      border-color: rgba(255,107,107,0.35);
      color: #ff8585;
    }

    /* Editor Area */
    .editor-wrap {
      flex: 1;
      overflow-y: auto;
      padding: 40px 10vw;
      max-width: 900px;
      margin: 0 auto;
      width: 100%;
    }
    .editor-wrap::-webkit-scrollbar { width: 8px; }
    .editor-wrap::-webkit-scrollbar-track { background: transparent; }
    .editor-wrap::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 10px; }

    /* Title input */
    .note-editor-title {
      width: 100%;
      background: transparent;
      border: none;
      outline: none;
      font-family: 'Instrument Serif', Georgia, serif;
      font-size: 42px;
      color: #fff;
      padding: 8px 0;
      margin-bottom: 6px;
      letter-spacing: -0.03em;
      line-height: 1.15;
      caret-color: var(--accent);
    }
    .note-editor-title::placeholder { color: var(--text-muted); opacity: 0.6; }

    /* Tags input row */
    .tags-editor {
      display: flex;
      gap: 8px;
      align-items: center;
      margin-bottom: 32px;
      flex-wrap: wrap;
    }
    .tags-editor .tag-input {
      padding: 6px 10px;
      background: var(--surface-2);
      border: 1px solid var(--border);
      border-radius: 8px;
      color: var(--text);
      font-size: 13px;
      outline: none;
      transition: all 0.15s ease;
    }
    .tags-editor .tag-input:focus {
      border-color: var(--accent);
      box-shadow: 0 0 0 3px var(--accent-soft);
    }

    /* Content textarea */
    .note-editor-body {
      width: 100%;
      min-height: 60vh;
      resize: none;
      background: transparent;
      border: none;
      outline: none;
      color: var(--text);
      font-family: 'Inter', sans-serif;
      font-size: 16px;
      line-height: 1.75;
      letter-spacing: -0.01em;
    }
    .note-editor-body::placeholder { color: var(--text-muted); opacity: 0.5; }

    /* Preview mode */
    .preview-mode {
      font-size: 16px;
      line-height: 1.75;
      color: var(--text);
    }
    .preview-mode h1, .preview-mode h2, .preview-mode h3 {
      font-family: 'Instrument Serif', Georgia, serif;
      color: #fff;
      margin-top: 36px;
      margin-bottom: 14px;
      letter-spacing: -0.02em;
      line-height: 1.2;
    }
    .preview-mode h1 { font-size: 32px; }
    .preview-mode h2 { font-size: 24px; }
    .preview-mode h3 { font-size: 20px; }
    .preview-mode p { margin-bottom: 20px; color: var(--text); }
    .preview-mode ul, .preview-mode ol { margin-bottom: 20px; padding-left: 24px; }
    .preview-mode li { margin-bottom: 6px; }
    .preview-mode blockquote {
      margin: 24px 0;
      padding: 16px 20px;
      border-left: 3px solid var(--accent);
      background: linear-gradient(90deg, var(--accent-soft), transparent);
      border-radius: 0 10px 10px 0;
      font-style: italic;
      color: var(--text-dim);
    }
    .preview-mode code {
      font-family: 'JetBrains Mono', monospace;
      background: var(--surface-2);
      padding: 2px 6px;
      border-radius: 4px;
      font-size: 0.9em;
      color: var(--success);
    }
    .preview-mode pre {
      background: var(--surface-2);
      padding: 16px;
      border-radius: 12px;
      overflow-x: auto;
      border: 1px solid var(--border);
      margin-bottom: 20px;
    }
    .preview-mode pre code {
      background: none;
      padding: 0;
      color: var(--text);
      font-size: 14px;
      line-height: 1.6;
    }
    .preview-mode hr {
      border: none;
      border-top: 1px solid var(--border);
      margin: 32px 0;
    }

    /* Empty state */
    .empty-state {
      text-align: center;
      padding: 80px 20px;
      color: var(--text-muted);
    }
    .empty-state i { font-size: 48px; margin-bottom: 24px; display: block; opacity: 0.3; }
    .empty-state h2 { font-family: 'Instrument Serif', Georgia, serif; font-size: 28px; color: var(--text-dim); margin-bottom: 8px; }
    .empty-state p { font-size: 14px; max-width: 320px; margin: 0 auto; }

    /* Footer of editor */
    .editor-footer {
      margin-top: 40px;
      padding-top: 20px;
      border-top: 1px solid var(--border);
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 12px;
      color: var(--text-muted);
    }

    /* Responsive */
    @media (max-width: 960px) {
      .sidebar { position: fixed; left: 0; top: 0; bottom: 0; width: 280px; transform: translateX(-100%); transition: transform 0.3s ease; z-index: 50; }
      .sidebar.open { transform: translateX(0); box-shadow: 20px 0 50px rgba(0,0,0,0.6); }
      .main { padding-left: 0; }
      .editor-wrap { padding: 32px 24px; }
    }
  </style>
</head>
<body>

<div class="app">
  <!-- Sidebar -->
  <aside class="sidebar" id="sidebar">
    <div class="brand">
      <div class="brand-icon"><i class="fa-solid fa-feather-pointed"></i></div>
      <div>
        <h1>Vault</h1>
        <span>Notes & Ideas</span>
      </div>
    </div>

    <div class="search-box">
      <i class="fa-solid fa-magnifying-glass"></i>
      <input type="text" id="searchInput" placeholder="Search notes..." autocomplete="off" />
    </div>

    <div class="note-list" id="noteList">
      <!-- Populated by JS -->
    </div>
  </aside>

  <!-- Main Content -->
  <main class="main">
    <div class="toolbar">
      <div class="toolbar-left">
        <button class="btn" onclick="toggleSidebar()" aria-label="Menu">
          <i class="fa-solid fa-bars"></i>
        </button>
        <div>
          <h2 id="toolbarTitle">Select a note</h2>
          <div class="toolbar-sub" id="toolbarSub">Create or choose from the sidebar</div>
        </div>
      </div>
      <div class="toolbar-right">
        <button class="btn" onclick="togglePreview()" id="previewBtn" title="Toggle Preview">
          <i class="fa-regular fa-eye"></i> Preview
        </button>
        <button class="btn btn-primary" onclick="newNote()" title="New Note">
          <i class="fa-solid fa-plus"></i> New
        </button>
        <button class="btn btn-danger" onclick="deleteNote()" id="deleteBtn" style="display:none" title="Delete Note">
          <i class="fa-regular fa-trash-can"></i>
        </button>
      </div>
    </div>

    <div class="editor-wrap" id="editorWrap">
      <!-- Empty state initially -->
      <div class="empty-state" id="emptyState">
        <i class="fa-solid fa-book-open"></i>
        <h2>Nothing selected</h2>
        <p>Create a new note or select one from the sidebar to begin writing.</p>
      </div>

      <div id="editorContent" style="display:none;">
        <input type="text" class="note-editor-title" id="noteTitle" placeholder="Untitled Note" autocomplete="off" />
        <div class="tags-editor" id="tagsRow">
          <input type="text" class="tag-input" id="tagInput" placeholder="Add tag... (enter to add)" autocomplete="off" onkeydown="handleTagKey(event)" />
        </div>
        <textarea class="note-editor-body" id="noteBody" placeholder="Write your thoughts..." autocomplete="off"></textarea>
        <div class="editor-footer">
          <span id="wordCount">0 words</span>
          <span>Markdown + preview supported</span>
        </div>
      </div>

      <div id="previewContent" style="display:none;" class="preview-mode"></div>
    </div>
  </main>
</div>

<script>
  // --- Data ---
  let notes = [
    {
      id: 1,
      title: "The Architecture of Thought",
      body: "Every idea begins as a fragile spark. To capture it is to give it architecture — a structure that outlasts the moment of inspiration.\n\n# Principles\n- Clarity over complexity.\n- Brevity is elegance.\n- A single thought, well held, is worth more than a thousand fragments.\n\n> \"Notes are not memory. They are the scaffolding for memory.\"",
      tags: ["philosophy", "writing"],
      updated: "2 hours ago"
    },
    {
      id: 2,
      title: "Design Systems at Scale",
      body: "When building interfaces, consistency creates trust. A design system is a shared language between product and user.\n\nKey pillars:\n1. **Tokens** for color, spacing, typography.\n2. **Components** built on those tokens.\n3. **Patterns** that solve recurring problems.\n\nKeep it alive. A dead design system is just documentation.",
      tags: ["design", "systems"],
      updated: "1 day ago"
    },
    {
      id: 3,
      title: "Morning Pages",
      body: "Three pages of stream-of-consciousness writing, done first thing in the morning. No agenda. No audience.\n\nIt clears the mental fog and makes space for the real work.",
      tags: ["routine"],
      updated: "3 days ago"
    },
    {
      id: 4,
      title: "Reading List — Q1",
      body: "- The Design of Everyday Things\n- Atomic Habits\n- Thinking in Systems\n\nNotes for each coming soon.",
      tags: ["reading", "list"],
      updated: "5 days ago"
    }
  ];

  let activeId = 1;
  let previewMode = false;

  // --- Utilities ---
  function timeAgo(date) {
    const now = new Date();
    const diff = Math.round((now - new Date(date)) / 1000);
    if (diff < 60) return "Just now";
    if (diff < 3600) return Math.floor(diff / 60) + " min ago";
    if (diff < 86400) return Math.floor(diff / 3600) + " hours ago";
    return Math.floor(diff / 86400) + " days ago";
  }

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

  function renderMarkdown(text) {
    let html = escapeHtml(text);
    // Headers
    html = html.replace(/^### (.*$)/gim, '<h3>$1</h3>');
    html = html.replace(/^## (.*$)/gim, '<h2>$1</h2>');
    html = html.replace(/^# (.*$)/gim, '<h1>$1</h1>');
    // Bold & Italic
    html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
    html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
    // Links
    html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
    // Blockquote
    html = html.replace(/^> (.*$)/gim, '<blockquote>$1</blockquote>');
    // Lists
    html = html.replace(/^- (.*$)/gim, '<ul><li>$1</li></ul>');
    html = html.replace(/^\* (.*$)/gim, '<ul><li>$1</li></ul>');
    // Numbers
    html = html.replace(/^\d+\. (.*$)/gim, '<ol><li>$1</li></ol>');
    // Code inline
    html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
    // Clean up duplicate list tags (basic)
    html = html.replace(/<\/ul>\s*<ul>/g, '');
    html = html.replace(/<\/ol>\s*<ol>/g, '');
    // Line breaks to paragraphs
    html = html.split(/\n\n+/).map(block => {
      if (/<(h[1-3]|ul|ol|blockquote|pre)/.test(block)) return block;
      return '<p>' + block.replace(/\n/g, '<br>') + '</p>';
    }).join('');
    return html;
  }

  function countWords(str) {
    return str.trim() ? str.trim().split(/\s+/).length : 0;
  }

  // --- Rendering ---
  function renderSidebar() {
    const list = document.getElementById('noteList');
    const search = document.getElementById('searchInput').value.toLowerCase();

    const filtered = notes.filter(n => {
      const match = n.title.toLowerCase().includes(search) || 
                    n.body.toLowerCase().includes(search) ||
                    n.tags.some(t => t.toLowerCase().includes(search));
      return match;
    });

    if (filtered.length === 0) {
      list.innerHTML = `<div style="padding: 20px; text-align:center; color:var(--text-muted); font-size:13px;">No notes found.</div>`;
      return;
    }

    list.innerHTML = filtered.map(n => {
      const isActive = n.id === activeId;
      const tagsHtml = n.tags.map(t => `<span class="tag">${t}</span>`).join('');
      const previewText = n.body.replace(/\n/g, ' ').slice(0, 90) + (n.body.length > 90 ? '...' : '');
      return `
        <div class="note-card ${isActive ? 'active' : ''}" onclick="selectNote(${n.id})" data-id="${n.id}">
          <div class="note-title">${escapeHtml(n.title)}</div>
          <div class="note-preview">${escapeHtml(previewText)}</div>
          <div class="note-meta">
            <span>${n.updated}</span>
          </div>
          <div class="tags">${tagsHtml}</div>
        </div>
      `;
    }).join('');
  }

  // --- Actions ---
  function selectNote(id) {
    activeId = id;
    renderSidebar();
    renderEditor();
    document.getElementById('deleteBtn').style.display = 'inline-flex';
  }

  function newNote() {
    const newId = Date.now();
    const note = {
      id: newId,
      title: "Untitled Note",
      body: "",
      tags: [],
      updated: "Just now"
    };
    notes.unshift(note);
    activeId = newId;
    renderSidebar();
    renderEditor();
    document.getElementById('deleteBtn').style.display = 'inline-flex';
    document.getElementById('noteTitle').focus();
  }

  function deleteNote() {
    if (!confirm("Delete this note?")) return;
    notes = notes.filter(n => n.id !== activeId);
    activeId = notes.length ? notes[0].id : null;
    renderSidebar();
    renderEditor();
  }

  function togglePreview() {
    previewMode = !previewMode;
    if (previewMode) {
      document.getElementById('editorContent').style.display = 'none';
      document.getElementById('previewContent').style.display = 'block';
      document.getElementById('previewBtn').innerHTML = '<i class="fa-solid fa-pen"></i> Edit';
    } else {
      document.getElementById('editorContent').style.display = 'block';
      document.getElementById('previewContent').style.display = 'none';
      document.getElementById('previewBtn').innerHTML = '<i class="fa-regular fa-eye"></i> Preview';
    }
    renderPreview();
  }

  function renderEditor() {
    const note = notes.find(n => n.id === activeId);
    const emptyState = document.getElementById('emptyState');
    const editorContent = document.getElementById('editorContent');

    if (!note) {
      emptyState.style.display = 'block';
      editorContent.style.display = 'none';
      document.getElementById('toolbarTitle').textContent = "Select a note";
      document.getElementById('toolbarSub').textContent = "Create or choose from the sidebar";
      document.getElementById('deleteBtn').style.display = 'none';
      return;
    }

    emptyState.style.display = 'none';
    editorContent.style.display = 'block';

    document.getElementById('toolbarTitle').textContent = note.title;
    document.getElementById('toolbarSub').textContent = `${note.tags.join(', ') || 'No tags'} • ${note.updated}`;

    const titleInput = document.getElementById('noteTitle');
    const bodyInput = document.getElementById('noteBody');

    // Only update value if focused out previously, or different, to avoid cursor jumps
    if (document.activeElement !== titleInput) titleInput.value = note.title;
    if (document.activeElement !== bodyInput) bodyInput.value = note.body;

    // Tags
    renderTags();

    document.getElementById('wordCount').textContent = countWords(note.body) + " words";
  }

  function renderTags() {
    const note = notes.find(n => n.id === activeId);
    if (!note) return;
    const row = document.getElementById('tagsRow');
    // Keep input, add tags before it
    const tagsDiv = document.createElement('div');
    tagsDiv.style.display = 'flex';
    tagsDiv.style.gap = '6px';
    tagsDiv.style.flexWrap = 'wrap';
    tagsDiv.innerHTML = note.tags.map(t => `
      <span class="tag" onclick="removeTag('${t}')" title="Remove tag" style="cursor:pointer;">
        ${t} <i class="fa-solid fa-xmark" style="margin-left:4px; font-size:10px;"></i>
      </span>
    `).join('');
    // We keep the input separately; instead we rebuild the inner HTML around the input
    // For simplicity, we place tags before the input manually
    const input = document.getElementById('tagInput');
    // Clear tags from row except input, then prepend tags
    while (row.firstChild !== input) {
      row.removeChild(row.firstChild);
    }
    note.tags.forEach(t => {
      const span = document.createElement('span');
      span.className = 'tag';
      span.style.cursor = 'pointer';
      span.innerHTML = `${t} <i class="fa-solid fa-xmark" style="margin-left:4px;font-size:10px;"></i>`;
      span.onclick = () => removeTag(t);
      row.insertBefore(span, input);
    });
  }

  function removeTag(tag) {
    const note = notes.find(n => n.id === activeId);
    if (!note) return;
    note.tags = note.tags.filter(t => t !== tag);
    note.updated = "Just now";
    renderTags();
    renderSidebar();
  }

  function handleTagKey(e) {
    if (e.key === 'Enter' || e.key === ',') {
      e.preventDefault();
      const val = e.target.value.trim();
      if (!val) return;
      const note = notes.find(n => n.id === activeId);
      if (note && !note.tags.includes(val)) {
        note.tags.push(val);
        note.updated = "Just now";
      }
      e.target.value = "";
      renderTags();
      renderSidebar();
    }
  }

  // Input handlers with debounce
  let saveTimeout;
  function onEdit() {
    const note = notes.find(n => n.id === activeId);
    if (!note) return;
    note.title = document.getElementById('noteTitle').value;
    note.body = document.getElementById('noteBody').value;
    note.updated = "Just now";
    clearTimeout(saveTimeout);
    saveTimeout = setTimeout(() => {
      renderSidebar();
      document.getElementById('toolbarTitle').textContent = note.title;
      document.getElementById('toolbarSub').textContent = `${note.tags.join(', ') || 'No tags'} • Just now`;
      document.getElementById('wordCount').textContent = countWords(note.body) + " words";
      // Also update preview if visible
      if (previewMode) renderPreview();
    }, 300);
  }

  function renderPreview() {
    const note = notes.find(n => n.id === activeId);
    if (!note) return;
    document.getElementById('previewContent').innerHTML = renderMarkdown(note.body);
  }

  // Init bindings
  document.getElementById('noteTitle').addEventListener('input', onEdit);
  document.getElementById('noteBody').addEventListener('input', onEdit);
  document.getElementById('searchInput').addEventListener('input', () => {
    renderSidebar();
  });

  // Mobile sidebar toggle
  function toggleSidebar() {
    document.getElementById('sidebar').classList.toggle('open');
  }

  // Initialize
  renderSidebar();
  renderEditor();
</script>

</body>
</html>
<!-- agent-meta {"model":"thinkingmachines/inkling","provider":"openrouter","persona":"basic","sessionId":"0f57884f-2ae8-46c1-8d42-a6ef9cd6fdf5","tokensIn":222,"tokensOut":7665,"tokensTotal":7887,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-07-18T20:31:08.979Z"} -->