← Notes results

Notes

thinkingcap qwen3.6 27b · full

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 34,204 bytes · SHA-256 b7d01670369f
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NoteFlow — Notes App</title>
<style>
  *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
  :root{
    --bg-primary:#ffffff;--bg-secondary:#f8f9fa;--bg-tertiary:#eef1f5;
    --bg-hover:#e2e6ea;--bg-active:#d4d9df;--bg-sidebar:#fafbfc;
    --text-primary:#1a1a2e;--text-secondary:#555770;--text-tertiary:#8b8da3;
    --accent:#6c5ce7;--accent-light:#a29bfe;--accent-bg:rgba(108,92,231,.08);
    --border:#e4e7ec;--shadow:0 2px 12px rgba(0,0,0,.06);
    --radius:8px;--radius-lg:12px;
    --font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
    --font-mono:'JetBrains Mono','Fira Code',monospace;
    --sidebar-w:260px;--note-list-w:280px;
    --transition:180ms ease;
  }
  [data-theme="dark"]{
    --bg-primary:#1a1b2e;--bg-secondary:#232439;--bg-tertiary:#2c2d45;
    --bg-hover:#363750;--bg-active:#404160;--bg-sidebar:#18192a;
    --text-primary:#e8e8f0;--text-secondary:#a0a0b8;--text-tertiary:#6e6e88;
    --accent:#a29bfe;--accent-light:#c4bfff;--accent-bg:rgba(162,155,254,.1);
    --border:#333450;--shadow:0 2px 12px rgba(0,0,0,.25);
  }
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
  html,body{height:100%;font-family:var(--font-sans);background:var(--bg-primary);color:var(--text-primary);overflow:hidden;font-size:14px}

  /* ── Layout ── */
  .app{display:flex;height:100vh;width:100vw;position:relative}
  
  /* ── Sidebar (Folders) ── */
  .sidebar{width:var(--sidebar-w);min-width:var(--sidebar-w);background:var(--bg-sidebar);border-right:1px solid var(--border);display:flex;flex-direction:column;transition:min-width .25s ease,width .25s ease,opacity .2s;z-index:20}
  .sidebar.collapsed{width:0;min-width:0;overflow:hidden;border:none}
  .sidebar-header{padding:16px;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--border)}
  .logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:15px;color:var(--accent);white-space:nowrap}
  .logo svg{flex-shrink:0}
  .sidebar-toggle{margin-left:auto;background:none;border:none;color:var(--text-tertiary);cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center}
  .sidebar-toggle:hover{background:var(--bg-hover)}

  .search-box{padding:12px 16px}
  .search-input{width:100%;padding:8px 12px 8px 34px;border-radius:var(--radius);border:1px solid var(--border);background:var(--bg-secondary);color:var(--text-primary);font-size:13px;outline:none;transition:border var(--transition)}
  .search-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-bg)}
  .search-wrap{position:relative}
  .search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--text-tertiary);pointer-events:none}

  .folder-list{flex:1;overflow-y:auto;padding:4px 8px}
  .folder-item{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:var(--radius);cursor:pointer;font-size:13px;color:var(--text-secondary);transition:background var(--transition),color var(--transition);user-select:none;white-space:nowrap}
  .folder-item:hover{background:var(--bg-hover)}
  .folder-item.active{background:var(--accent-bg);color:var(--accent);font-weight:600}
  .folder-icon{width:18px;height:18px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:14px}
  .folder-count{margin-left:auto;font-size:11px;color:var(--text-tertiary);background:var(--bg-tertiary);padding:1px 7px;border-radius:10px}

  .sidebar-footer{padding:12px 16px;border-top:1px solid var(--border);display:flex;gap:4px}
  .footer-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:8px;border:none;background:var(--bg-secondary);color:var(--text-secondary);border-radius:var(--radius);cursor:pointer;font-size:12px;font-weight:500;transition:all var(--transition)}
  .footer-btn:hover{background:var(--bg-hover);color:var(--text-primary)}

  /* ── Note List Panel ── */
  .note-list-panel{width:var(--note-list-w);min-width:var(--note-list-w);border-right:1px solid var(--border);display:flex;flex-direction:column;background:var(--bg-secondary);transition:min-width .25s ease,width .25s ease,opacity .2s;z-index:10}
  .note-list-panel.collapsed{width:0;min-width:0;overflow:hidden;border:none}
  .note-list-header{padding:16px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border)}
  .note-list-title{font-weight:600;font-size:14px;color:var(--text-primary)}
  .new-note-btn{background:var(--accent);color:#fff;border:none;padding:6px 14px;border-radius:var(--radius);cursor:pointer;font-size:12px;font-weight:600;display:flex;align-items:center;gap:5px;transition:all var(--transition)}
  .new-note-btn:hover{background:var(--accent-light);transform:translateY(-1px);box-shadow:0 4px 12px rgba(108,92,231,.3)}

  .note-list{flex:1;overflow-y:auto;padding:6px}
  .note-card{padding:12px;border-radius:var(--radius-lg);cursor:pointer;margin-bottom:4px;transition:all var(--transition);border:1px solid transparent}
  .note-card:hover{background:var(--bg-hover)}
  .note-card.active{background:var(--accent-bg);border-color:var(--accent);box-shadow:0 2px 8px rgba(108,92,231,.1)}
  .note-card-title{font-weight:600;font-size:13px;margin-bottom:4px;color:var(--text-primary);line-height:1.3;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}
  .note-card-preview{font-size:12px;color:var(--text-tertiary);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:6px}
  .note-card-meta{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--text-tertiary)}
  .note-tag{background:var(--bg-tertiary);padding:1px 6px;border-radius:4px;font-size:10px}

  /* ── Editor Area ── */
  .editor-area{flex:1;display:flex;flex-direction:column;background:var(--bg-primary);min-width:0}
  
  .toolbar{padding:8px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:4px;background:var(--bg-primary);flex-shrink:0;overflow-x:auto}
  .tool-btn{background:none;border:none;color:var(--text-secondary);cursor:pointer;padding:6px 8px;border-radius:6px;display:flex;align-items:center;justify-content:center;transition:all var(--transition);font-size:13px;min-width:32px;height:32px}
  .tool-btn:hover{background:var(--bg-hover);color:var(--text-primary)}
  .tool-btn.active{background:var(--accent-bg);color:var(--accent)}
  .tool-sep{width:1px;height:20px;background:var(--border);margin:0 4px;flex-shrink:0}
  .toolbar-spacer{flex:1}

  .editor-title-area{padding:28px 40px 8px;flex-shrink:0}
  .editor-title{font-size:28px;font-weight:700;color:var(--text-primary);border:none;background:none;width:100%;outline:none;line-height:1.3;resize:none;font-family:var(--font-sans)}
  .editor-title::placeholder{color:var(--text-tertiary)}

  .editor-meta{padding:0 40px 8px;display:flex;gap:12px;font-size:12px;color:var(--text-tertiary);align-items:center}
  .meta-tag{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;background:var(--bg-tertiary);border-radius:4px;cursor:pointer;transition:all var(--transition)}
  .meta-tag:hover{background:var(--bg-hover)}

  .editor-content-wrap{flex:1;overflow-y:auto;padding:0 40px 60px}
  .editor-content{outline:none;font-size:15px;line-height:1.75;color:var(--text-primary);min-height:200px}
  .editor-content h1{font-size:24px;margin:16px 0 8px;font-weight:700}
  .editor-content h2{font-size:20px;margin:14px 0 6px;font-weight:600}
  .editor-content h3{font-size:17px;margin:12px 0 4px;font-weight:600}
  .editor-content p{margin-bottom:8px}
  .editor-content ul,.editor-content ol{padding-left:24px;margin-bottom:8px}
  .editor-content blockquote{border-left:3px solid var(--accent);padding-left:16px;color:var(--text-secondary);margin:8px 0;font-style:italic}
  .editor-content code{background:var(--bg-tertiary);padding:2px 6px;border-radius:4px;font-family:var(--font-mono);font-size:13px}
  .editor-content pre{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius);padding:12px;margin:8px 0;overflow-x:auto}
  .editor-content pre code{background:none;padding:0}
  .editor-content a{color:var(--accent)}
  .editor-content hr{border:none;border-top:1px solid var(--border);margin:16px 0}

  /* ── Empty State ── */
  .empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-tertiary);gap:12px;padding:40px}
  .empty-icon{font-size:64px;opacity:.3}
  .empty-title{font-size:18px;font-weight:600;color:var(--text-secondary)}
  .empty-desc{font-size:13px;text-align:center;max-width:280px;line-height:1.5}

  /* ── Modal / Dialog ── */
  .modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:100;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .2s}
  .modal-overlay.show{opacity:1;pointer-events:auto}
  .modal{background:var(--bg-primary);border-radius:var(--radius-lg);padding:24px;width:90%;max-width:400px;box-shadow:0 20px 60px rgba(0,0,0,.2);transform:scale(.95);transition:transform .2s}
  .modal-overlay.show .modal{transform:scale(1)}
  .modal h3{margin-bottom:16px;font-size:16px}
  .modal input[type="text"]{width:100%;padding:10px 14px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-secondary);color:var(--text-primary);font-size:14px;outline:none;margin-bottom:16px}
  .modal input:focus{border-color:var(--accent)}
  .modal-actions{display:flex;gap:8px;justify-content:flex-end}
  .modal-btn{padding:8px 20px;border-radius:var(--radius);border:none;cursor:pointer;font-size:13px;font-weight:600;transition:all var(--transition)}
  .modal-btn.primary{background:var(--accent);color:#fff}
  .modal-btn.primary:hover{background:var(--accent-light)}
  .modal-btn.secondary{background:var(--bg-tertiary);color:var(--text-secondary)}

  /* ── Context Menu ── */
  .context-menu{position:fixed;background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 8px 30px rgba(0,0,0,.15);padding:4px;z-index:200;min-width:160px;display:none}
  .context-menu.show{display:block}
  .ctx-item{padding:7px 12px;font-size:13px;color:var(--text-secondary);cursor:pointer;border-radius:4px;display:flex;align-items:center;gap:8px;transition:background var(--transition)}
  .ctx-item:hover{background:var(--bg-hover);color:var(--text-primary)}
  .ctx-item.danger{color:#e74c3c}
  .ctx-sep{height:1px;background:var(--border);margin:4px 0}

  /* ── Toast ── */
  .toast-container{position:fixed;bottom:20px;right:20px;z-index:300;display:flex;flex-direction:column;gap:8px}
  .toast{padding:12px 20px;background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);font-size:13px;color:var(--text-primary);display:flex;align-items:center;gap:8px;animation:toastIn .3s ease,toastOut .3s ease 2.7s forwards}
  @keyframes toastIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
  @keyframes toastOut{to{opacity:0;transform:translateX(20px)}}

  /* ── Scrollbar ── */
  ::-webkit-scrollbar{width:6px}
  ::-webkit-scrollbar-track{background:transparent}
  ::-webkit-scrollbar-thumb{background:var(--bg-active);border-radius:3px}
  ::-webkit-scrollbar-thumb:hover{background:var(--text-tertiary)}

  /* ── Mobile Toggle ── */
  .mobile-toggle{display:none;position:fixed;top:12px;left:12px;z-index:50;background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius);padding:8px;cursor:pointer;color:var(--text-secondary)}

  @media(max-width:768px){
    .sidebar{position:absolute;left:0;top:0;height:100%;box-shadow:none}
    .note-list-panel{position:absolute;left:var(--sidebar-w);top:0;height:100%}
    .mobile-toggle{display:flex}
  }

  /* ── Animations ── */
  @keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
  .note-card{animation:fadeIn .2s ease}
</style>
</head>
<body>

<div class="app" id="app">
  <!-- Sidebar -->
  <aside class="sidebar" id="sidebar">
    <div class="sidebar-header">
      <div class="logo">
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none"><rect x="3" y="2" width="18" height="20" rx="3" stroke="currentColor" stroke-width="2"/><path d="M8 7h8M8 11h5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
        NoteFlow
      </div>
      <button class="sidebar-toggle" onclick="toggleSidebar()" title="Toggle sidebar">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
      </button>
    </div>

    <div class="search-box">
      <div class="search-wrap">
        <span class="search-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg></span>
        <input class="search-input" type="text" placeholder="Search notes…" id="searchInput" oninput="filterNotes()">
      </div>
    </div>

    <div class="folder-list" id="folderList"></div>

    <div class="sidebar-footer">
      <button class="footer-btn" onclick="openNewFolderModal()">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
        New Folder
      </button>
      <button class="footer-btn" onclick="toggleTheme()" title="Toggle theme">
        <span id="themeIcon">🌙</span>
      </button>
    </div>
  </aside>

  <!-- Note List -->
  <div class="note-list-panel" id="noteListPanel">
    <div class="note-list-header">
      <span class="note-list-title" id="folderTitle">All Notes</span>
      <button class="new-note-btn" onclick="createNote()">
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M12 5v14M5 12h14"/></svg>
        New Note
      </button>
    </div>
    <div class="note-list" id="noteList"></div>
  </div>

  <!-- Editor -->
  <main class="editor-area" id="editorArea">
    <div class="empty-state" id="emptyState">
      <div class="empty-icon">📝</div>
      <div class="empty-title">No note selected</div>
      <div class="empty-desc">Select a note from the sidebar or create a new one to get started.</div>
    </div>

    <div id="editorContainer" style="display:none;flex:1;display:none;flex-direction:column;">
      <div class="toolbar" id="toolbar">
        <button class="tool-btn" onclick="execCmd('bold')" title="Bold (Ctrl+B)"><b>B</b></button>
        <button class="tool-btn" onclick="execCmd('italic')" title="Italic (Ctrl+I)"><i>I</i></button>
        <button class="tool-btn" onclick="execCmd('underline')" title="Underline (Ctrl+U)" style="text-decoration:underline">U</button>
        <button class="tool-btn" onclick="execCmd('strikeThrough')" title="Strikethrough" style="text-decoration:line-through">S</button>
        <div class="tool-sep"></div>
        <button class="tool-btn" onclick="execHeading('h1')" title="Heading 1">H1</button>
        <button class="tool-btn" onclick="execHeading('h2')" title="Heading 2">H2</button>
        <button class="tool-btn" onclick="execHeading('h3')" title="Heading 3">H3</button>
        <div class="tool-sep"></div>
        <button class="tool-btn" onclick="execCmd('insertUnorderedList')" title="Bullet list">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="9" y1="6" x2="20" y2="6"/><line x1="9" y1="12" x2="20" y2="12"/><line x1="9" y1="18" x2="20" y2="18"/><circle cx="5" cy="6" r="1.5" fill="currentColor"/><circle cx="5" cy="12" r="1.5" fill="currentColor"/><circle cx="5" cy="18" r="1.5" fill="currentColor"/></svg>
        </button>
        <button class="tool-btn" onclick="execCmd('insertOrderedList')" title="Numbered list">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="10" y1="6" x2="20" y2="6"/><line x1="10" y1="12" x2="20" y2="12"/><line x1="10" y1="18" x2="20" y2="18"/></svg>
        </button>
        <div class="tool-sep"></div>
        <button class="tool-btn" onclick="execCmd('formatBlock','<blockquote>')" title="Quote">❝</button>
        <button class="tool-btn" onclick="insertCode()" title="Inline code">&lt;/&gt;</button>
        <button class="tool-btn" onclick="execCmd('insertHorizontalRule')" title="Divider">—</button>
        <div class="toolbar-spacer"></div>
        <span id="wordCount" style="font-size:11px;color:var(--text-tertiary)"></span>
      </div>

      <div class="editor-title-area">
        <input class="editor-title" type="text" id="noteTitleInput" placeholder="Untitled note…" oninput="updateNoteTitle()">
      </div>

      <div class="editor-meta" id="editorMeta"></div>

      <div class="editor-content-wrap">
        <div class="editor-content" id="editorContent" contenteditable="true" spellcheck="true" oninput="onEditorInput()" onkeydown="handleEditorKeys(event)"></div>
      </div>
    </div>
  </main>
</div>

<!-- Context Menu -->
<div class="context-menu" id="contextMenu">
  <div class="ctx-item" onclick="duplicateNote()">📋 Duplicate</div>
  <div class="ctx-item" onclick="moveNoteFolder()">📁 Move to…</div>
  <div class="ctx-sep"></div>
  <div class="ctx-item danger" onclick="deleteCurrentNote()">🗑 Delete</div>
</div>

<!-- New Folder Modal -->
<div class="modal-overlay" id="folderModal">
  <div class="modal">
    <h3>New Folder</h3>
    <input type="text" id="newFolderName" placeholder="Folder name…" onkeydown="if(event.key==='Enter')saveNewFolder()">
    <div class="modal-actions">
      <button class="modal-btn secondary" onclick="closeModal('folderModal')">Cancel</button>
      <button class="modal-btn primary" onclick="saveNewFolder()">Create</button>
    </div>
  </div>
</div>

<!-- Move Note Modal -->
<div class="modal-overlay" id="moveNoteModal">
  <div class="modal">
    <h3>Move to Folder</h3>
    <div id="moveFolderList" style="max-height:200px;overflow-y:auto;margin-bottom:16px"></div>
    <div class="modal-actions">
      <button class="modal-btn secondary" onclick="closeModal('moveNoteModal')">Cancel</button>
    </div>
  </div>
</div>

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

<script>
// ── Data Layer ──
const STORAGE_KEY = 'noteflow_data';
let state = {
  folders: [
    { id: 'personal', name: 'Personal', icon: '📁' },
    { id: 'work', name: 'Work', icon: '💼' },
    { id: 'ideas', name: 'Ideas', icon: '💡' },
  ],
  notes: [
    {
      id: 'n1', title: 'Welcome to NoteFlow ✨', folderId: 'personal',
      content: '<h2>Welcome!</h2><p>NoteFlow is a beautiful, minimal notes application. Here are some things you can do:</p><ul><li>Create and organize notes into folders</li><li>Format text with the toolbar above</li><li>Search through all your notes</li><li>Toggle dark mode for comfortable reading</li></ul><blockquote>Your data is saved automatically in your browser.</blockquote><h3>Getting Started</h3><p>Click <strong>New Note</strong> to create your first note, or select one from the sidebar. Everything you type is auto-saved!</p>',
      tags: ['welcome', 'getting-started'], created: Date.now() - 86400000, updated: Date.now() - 3600000
    },
    {
      id: 'n2', title: 'Meeting Notes — Q4 Planning', folderId: 'work',
      content: '<h2>Q4 Planning Meeting</h2><p><strong>Date:</strong> October 15, 2024</p><ol><li>Review Q3 results and key metrics</li><li>Set OKRs for Q4</li><li>Budget allocation discussion</li></ol><p><strong>Action items:</strong></p><ul><li>Draft proposal by Friday</li><li>Schedule follow-up with design team</li><li>Prepare budget spreadsheet</li></ul>',
      tags: ['meeting', 'planning'], created: Date.now() - 172800000, updated: Date.now() - 7200000
    },
    {
      id: 'n3', title: 'App Ideas Backlog', folderId: 'ideas',
      content: '<h2>App Ideas</h2><p>A collection of ideas to explore:</p><h3>1. Habit Tracker</h3><p>A minimal habit tracking app with streaks and gentle reminders.</p><h3>2. Recipe Manager</h3><p>Organize recipes by cuisine, difficulty, and ingredients. Auto-generate shopping lists.</p><h3>3. Reading List</h3><p>Track books you want to read, currently reading, and finished. Rate and review.</p>',
      tags: ['ideas', 'apps'], created: Date.now() - 259200000, updated: Date.now() - 86400000
    },
    {
      id: 'n4', title: 'Weekly Journal — Week 42', folderId: 'personal',
      content: '<p>Great week overall. Finished the project presentation and got positive feedback from the team.</p><p>Started reading "Atomic Habits" — really enjoying it so far. Key takeaway: small changes compound over time.</p><hr><p><em>Gratitude:</em></p><ul><li>Good health this week</li><li>Creative flow on Thursday</li><li>Nice conversation with an old friend</li></ul>',
      tags: ['journal', 'weekly'], created: Date.now() - 345600000, updated: Date.now() - 172800000
    },
    {
      id: 'n5', title: 'Design System Notes', folderId: 'work',
      content: '<h2>Design System</h2><p>Key principles for our design system:</p><ul><li><strong>Consistency</strong> — Same patterns across all components</li><li><strong>Accessibility</strong> — WCAG 2.1 AA minimum</li><li><strong>Flexibility</strong> — Adaptable to different contexts</li></ul><h3>Color Palette</h3><p>Primary: <code>#6c5ce7</code>, Secondary: <code>#00b894</code>, Accent: <code>#fdcb6e</code></p>',
      tags: ['design', 'system'], created: Date.now() - 432000000, updated: Date.now() - 259200000
    }
  ],
  activeFolder: null, // null = all notes
  activeNoteId: null,
  theme: 'light'
};

function loadState() {
  try {
    const saved = localStorage.getItem(STORAGE_KEY);
    if (saved) {
      const parsed = JSON.parse(saved);
      state = { ...state, ...parsed };
    }
  } catch(e) {}
}

function saveState() {
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
  } catch(e) {}
}

// ── Rendering ──
function renderFolders() {
  const list = document.getElementById('folderList');
  let html = `<div class="folder-item ${state.activeFolder===null?'active':''}" onclick="selectFolder(null)">
    <span class="folder-icon">📋</span> All Notes
    <span class="folder-count">${state.notes.length}</span>
  </div>`;

  state.folders.forEach(f => {
    const count = state.notes.filter(n => n.folderId === f.id).length;
    html += `<div class="folder-item ${state.activeFolder===f.id?'active':''}" onclick="selectFolder('${f.id}')">
      <span class="folder-icon">${f.icon}</span> ${escHtml(f.name)}
      <span class="folder-count">${count}</span>
    </div>`;
  });

  list.innerHTML = html;
}

function renderNoteList() {
  const list = document.getElementById('noteList');
  let notes = state.notes;
  
  if (state.activeFolder !== null) {
    notes = notes.filter(n => n.folderId === state.activeFolder);
  }

  const query = document.getElementById('searchInput').value.toLowerCase().trim();
  if (query) {
    notes = notes.filter(n => 
      n.title.toLowerCase().includes(query) || 
      stripHtml(n.content).toLowerCase().includes(query) ||
      (n.tags||[]).some(t => t.toLowerCase().includes(query))
    );
  }

  // Sort by updated desc
  notes.sort((a, b) => b.updated - a.updated);

  if (!notes.length) {
    list.innerHTML = `<div style="text-align:center;padding:40px 20px;color:var(--text-tertiary)">
      <div style="font-size:32px;margin-bottom:8px">📭</div>
      <div style="font-size:13px">${query ? 'No notes match your search' : 'No notes in this folder'}</div>
    </div>`;
    return;
  }

  list.innerHTML = notes.map(n => {
    const preview = stripHtml(n.content).substring(0, 80);
    const date = formatDate(n.updated);
    const tags = (n.tags || []).slice(0, 2).map(t => `<span class="note-tag">${escHtml(t)}</span>`).join('');
    return `<div class="note-card ${state.activeNoteId===n.id?'active':''}" onclick="selectNote('${n.id}')" oncontextmenu="showContextMenu(event,'${n.id}')">
      <div class="note-card-title">${escHtml(n.title || 'Untitled')}</div>
      <div class="note-card-preview">${escHtml(preview)}</div>
      <div class="note-card-meta"><span>${date}</span>${tags}</div>
    </div>`;
  }).join('');

  const folder = state.folders.find(f => f.id === state.activeFolder);
  document.getElementById('folderTitle').textContent = folder ? folder.name : 'All Notes';
}

function renderEditor() {
  const emptyState = document.getElementById('emptyState');
  const editorContainer = document.getElementById('editorContainer');
  
  if (!state.activeNoteId) {
    emptyState.style.display = 'flex';
    editorContainer.style.display = 'none';
    return;
  }

  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) {
    state.activeNoteId = null;
    renderEditor();
    return;
  }

  emptyState.style.display = 'none';
  editorContainer.style.display = 'flex';

  document.getElementById('noteTitleInput').value = note.title || '';
  const editorContent = document.getElementById('editorContent');
  
  // Only update content if it differs (preserve cursor position when possible)
  if (editorContent.innerHTML !== note.content) {
    editorContent.innerHTML = note.content;
  }

  // Meta info
  const folder = state.folders.find(f => f.id === note.folderId);
  document.getElementById('editorMeta').innerHTML = `
    <span>📁 ${escHtml(folder ? folder.name : 'Unknown')}</span>
    <span>🕐 ${formatDate(note.updated)}</span>
    ${(note.tags||[]).map(t => `<span class="meta-tag" onclick="toggleTag('${escHtml(t)}')">✏️ ${escHtml(t)}</span>`).join('')}
  `;

  updateWordCount();
}

// ── Actions ──
function selectFolder(folderId) {
  state.activeFolder = folderId;
  renderFolders();
  renderNoteList();
}

function selectNote(noteId) {
  saveCurrentContent();
  state.activeNoteId = noteId;
  renderNoteList();
  renderEditor();
}

function createNote() {
  const id = 'n' + Date.now();
  const note = {
    id, title: '', folderId: state.activeFolder || 'personal',
    content: '<p>Start writing…</p>', tags: [],
    created: Date.now(), updated: Date.now()
  };
  state.notes.unshift(note);
  saveState();
  selectNote(id);
  setTimeout(() => document.getElementById('noteTitleInput').focus(), 100);
  showToast('✨ New note created');
}

function updateNoteTitle() {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.title = document.getElementById('noteTitleInput').value;
  note.updated = Date.now();
  saveState();
  renderNoteList(); // update preview in list
}

function onEditorInput() {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.content = document.getElementById('editorContent').innerHTML;
  note.updated = Date.now();
  saveState();
  updateWordCount();
}

function handleEditorKeys(e) {
  // Tab key inserts spaces
  if (e.key === 'Tab') {
    e.preventDefault();
    execCmd('insertText', '    ');
  }
}

function saveCurrentContent() {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.content = document.getElementById('editorContent').innerHTML;
  note.title = document.getElementById('noteTitleInput').value;
  note.updated = Date.now();
  saveState();
}

function deleteCurrentNote() {
  hideContextMenu();
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  state.notes = state.notes.filter(n => n.id !== state.activeNoteId);
  state.activeNoteId = null;
  saveState();
  renderFolders();
  renderNoteList();
  renderEditor();
  showToast('🗑 Note deleted');
}

function duplicateNote() {
  hideContextMenu();
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  const id = 'n' + Date.now();
  const dup = { ...note, id, title: note.title + ' (copy)', created: Date.now(), updated: Date.now() };
  state.notes.unshift(dup);
  saveState();
  selectNote(id);
  showToast('📋 Note duplicated');
}

function moveNoteFolder() {
  hideContextMenu();
  const list = document.getElementById('moveFolderList');
  list.innerHTML = state.folders.map(f => 
    `<div class="folder-item" onclick="doMoveNote('${f.id}')">${f.icon} ${escHtml(f.name)}</div>`
  ).join('');
  openModal('moveNoteModal');
}

function doMoveNote(folderId) {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.folderId = folderId;
  note.updated = Date.now();
  saveState();
  closeModal('moveNoteModal');
  renderFolders();
  renderNoteList();
  renderEditor();
  showToast('📁 Note moved');
}

function toggleTag(tag) {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note || !note.tags) return;
  const idx = note.tags.indexOf(tag);
  if (idx >= 0) note.tags.splice(idx, 1);
  else note.tags.push(tag);
  note.updated = Date.now();
  saveState();
  renderEditor();
  renderNoteList();
}

function filterNotes() {
  renderNoteList();
}

// ── Toolbar Commands ──
function execCmd(cmd, val) {
  document.execCommand(cmd, false, val || null);
  document.getElementById('editorContent').focus();
  onEditorInput();
}

function execHeading(tag) {
  document.execCommand('formatBlock', false, `<${tag}>`);
  document.getElementById('editorContent').focus();
  onEditorInput();
}

function insertCode() {
  const sel = window.getSelection();
  if (sel.rangeCount && !sel.isCollapsed) {
    const range = sel.getRangeAt(0);
    const code = document.createElement('code');
    code.textContent = range.toString();
    range.deleteContents();
    range.insertNode(code);
    onEditorInput();
  } else {
    execCmd('insertText', '`code`');
  }
}

function updateWordCount() {
  const text = stripHtml(document.getElementById('editorContent').innerHTML).trim();
  const words = text ? text.split(/\s+/).length : 0;
  document.getElementById('wordCount').textContent = `${words} word${words!==1?'s':''}`;
}

// ── Folders ──
function openNewFolderModal() {
  document.getElementById('newFolderName').value = '';
  openModal('folderModal');
  setTimeout(() => document.getElementById('newFolderName').focus(), 100);
}

function saveNewFolder() {
  const name = document.getElementById('newFolderName').value.trim();
  if (!name) return;
  const icons = ['📁','📂','🗂️','💼','🎨','🔬','📚','🏠','⭐','🎯'];
  state.folders.push({
    id: 'f' + Date.now(), name, icon: icons[Math.floor(Math.random()*icons.length)]
  });
  saveState();
  closeModal('folderModal');
  renderFolders();
  showToast(`📁 Folder "${name}" created`);
}

// ── Theme ──
function toggleTheme() {
  state.theme = state.theme === 'dark' ? 'light' : 'dark';
  applyTheme();
  saveState();
}

function applyTheme() {
  document.documentElement.setAttribute('data-theme', state.theme);
  document.getElementById('themeIcon').textContent = state.theme === 'dark' ? '☀️' : '🌙';
}

// ── Sidebar Toggle ──
function toggleSidebar() {
  const sb = document.getElementById('sidebar');
  sb.classList.toggle('collapsed');
}

// ── Context Menu ──
let ctxNoteId = null;
function showContextMenu(e, noteId) {
  e.preventDefault();
  e.stopPropagation();
  if (state.activeNoteId !== noteId) selectNote(noteId);
  const menu = document.getElementById('contextMenu');
  menu.style.left = e.clientX + 'px';
  menu.style.top = e.clientY + 'px';
  menu.classList.add('show');
}

function hideContextMenu() {
  document.getElementById('contextMenu').classList.remove('show');
}

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

// ── Modal Helpers ──
function openModal(id) { document.getElementById(id).classList.add('show'); }
function closeModal(id) { document.getElementById(id).classList.remove('show'); }

// Close modals on overlay click
document.querySelectorAll('.modal-overlay').forEach(el => {
  el.addEventListener('click', e => { if (e.target === el) el.classList.remove('show'); });
});

// ── Toast ──
function showToast(msg) {
  const container = document.getElementById('toastContainer');
  const toast = document.createElement('div');
  toast.className = 'toast';
  toast.textContent = msg;
  container.appendChild(toast);
  setTimeout(() => toast.remove(), 3000);
}

// ── Utilities ──
function escHtml(s) { const d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
function stripHtml(html) { const d=document.createElement('div'); d.innerHTML=html; return d.textContent||''; }
function formatDate(ts) {
  const d = new Date(ts);
  const now = new Date();
  const diff = now - d;
  if (diff < 60000) return 'Just now';
  if (diff < 3600000) return Math.floor(diff/60000) + 'm ago';
  if (diff < 86400000) return Math.floor(diff/3600000) + 'h ago';
  if (diff < 604800000) return Math.floor(diff/86400000) + 'd ago';
  return d.toLocaleDateString('en-US', { month:'short', day:'numeric' });
}

// ── Keyboard Shortcuts ──
document.addEventListener('keydown', e => {
  if ((e.ctrlKey || e.metaKey) && e.key === 'n') {
    e.preventDefault(); createNote();
  }
  if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
    e.preventDefault(); document.getElementById('searchInput').focus();
  }
  if (e.key === 'Escape') {
    document.querySelectorAll('.modal-overlay.show').forEach(m => m.classList.remove('show'));
  }
});

// ── Init ──
loadState();
applyTheme();
renderFolders();
renderNoteList();
renderEditor();
</script>
</body>
</html>
<!-- agent-meta {"model":"thinkingcap-qwen3.6-27b","provider":"lmstudio","persona":"full","sessionId":"aa49c281-625a-49f8-b5fe-3628188af482","tokensIn":65765,"tokensOut":11799,"tokensTotal":77564,"turns":5,"toolCalls":4,"failedToolCalls":0,"timestamp":"2026-07-18T20:13:10.838Z"} -->