← Notes results

Notes

thinkingcap qwen3.6 27b · 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 40,077 bytes · SHA-256 7183833287d5
<!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>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
--bg-primary:#ffffff;--bg-secondary:#f8f9fa;--bg-tertiary:#eef1f5;--bg-hover:#e8eaed;
--text-primary:#1a1a2e;--text-secondary:#555770;--text-tertiary:#8b8da3;
--accent:#6c5ce7;--accent-light:#a29bfe;--accent-bg:rgba(108,92,231,0.08);
--border:#e2e4e9;--shadow:0 2px 12px rgba(0,0,0,0.06);
--sidebar-width:280px;--toolbar-height:52px;
--radius:10px;--transition:0.2s ease;
--tag-colors:#6c5ce7,#00b894,#e17055,#0984e3,#fdcb6e,#d63031,#e84393,#00cec9;
}
[data-theme="dark"]{
--bg-primary:#1a1a2e;--bg-secondary:#16213e;--bg-tertiary:#1f2b47;--bg-hover:#253352;
--text-primary:#e8e8f0;--text-secondary:#9a9ab0;--text-tertiary:#6b6d80;
--accent:#a29bfe;--accent-light:#6c5ce7;--accent-bg:rgba(162,155,254,0.12);
--border:#2a3555;--shadow:0 2px 12px rgba(0,0,0,0.3);
}
html{font-size:15px}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg-primary);color:var(--text-primary);overflow:hidden;height:100vh;transition:background var(--transition),color var(--transition)}

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

/* Layout */
.app{display:flex;height:100vh;position:relative}

/* Sidebar */
.sidebar{width:var(--sidebar-width);min-width:var(--sidebar-width);background:var(--bg-secondary);border-right:1px solid var(--border);display:flex;flex-direction:column;transition:transform 0.3s ease,opacity 0.3s ease;z-index:100}
.sidebar.collapsed{transform:translateX(calc(-1 * var(--sidebar-width)));position:absolute;height:100%;opacity:0;pointer-events:none}

.sidebar-header{padding:20px 20px 12px;display:flex;align-items:center;justify-content:space-between}
.logo{display:flex;align-items:center;gap:10px;font-weight:700;font-size:1.2rem;color:var(--accent)}
.logo svg{width:28px;height:28px}

.sidebar-actions{display:flex;gap:4px}
.icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-secondary);transition:all var(--transition)}
.icon-btn:hover{background:var(--bg-hover);color:var(--text-primary)}

/* Search */
.search-box{margin:0 16px 12px;position:relative}
.search-box input{width:100%;padding:9px 14px 9px 38px;border:1.5px solid var(--border);border-radius:var(--radius);background:var(--bg-primary);color:var(--text-primary);font-size:0.88rem;font-family:inherit;outline:none;transition:border-color var(--transition)}
.search-box input:focus{border-color:var(--accent)}
.search-box svg{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--text-tertiary);width:16px;height:16px}

/* Folders */
.folders-section{padding:4px 12px;flex-shrink:0}
.section-label{font-size:0.7rem;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:var(--text-tertiary);padding:8px 8px 6px;display:flex;align-items:center;justify-content:space-between}
.folder-item{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:8px;cursor:pointer;font-size:0.88rem;color:var(--text-secondary);transition:all var(--transition);position:relative}
.folder-item:hover{background:var(--bg-hover);color:var(--text-primary)}
.folder-item.active{background:var(--accent-bg);color:var(--accent);font-weight:500}
.folder-item .count{margin-left:auto;font-size:0.72rem;background:var(--bg-tertiary);padding:1px 7px;border-radius:10px;color:var(--text-tertiary)}
.folder-item svg{width:16px;height:16px;flex-shrink:0}

/* Notes List */
.notes-list-section{flex:1;display:flex;flex-direction:column;overflow:hidden;margin-top:4px}
.notes-list{flex:1;overflow-y:auto;padding:0 8px}
.note-item{padding:12px 14px;border-radius:var(--radius);cursor:pointer;margin-bottom:2px;transition:all var(--transition);position:relative}
.note-item:hover{background:var(--bg-hover)}
.note-item.active{background:var(--accent-bg);border-left:3px solid var(--accent);margin-left:-3px;padding-left:17px}
.note-item-title{font-size:0.9rem;font-weight:500;margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--text-primary)}
.note-item-preview{font-size:0.78rem;color:var(--text-tertiary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.note-item-date{font-size:0.68rem;color:var(--text-tertiary);margin-top:4px}
.note-item-tags{display:flex;gap:4px;margin-top:5px;flex-wrap:wrap}
.mini-tag{font-size:0.62rem;padding:1px 6px;border-radius:4px;font-weight:500}

/* Main Content */
.main{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--bg-primary)}

/* Toolbar */
.toolbar{height:var(--toolbar-height);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 20px;gap:4px;flex-shrink:0}
.toolbar-group{display:flex;align-items:center;gap:2px;padding:0 6px;border-right:1px solid var(--border)}
.toolbar-group:last-child{border-right:none}
.tool-btn{width:34px;height:34px;border:none;background:transparent;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-secondary);transition:all var(--transition);font-size:0.85rem;font-weight:600}
.tool-btn:hover{background:var(--bg-hover);color:var(--text-primary)}
.tool-btn.active{background:var(--accent-bg);color:var(--accent)}
.tool-btn svg{width:18px;height:18px}

.toolbar-spacer{flex:1}

.note-title-input{font-size:1.6rem;font-weight:700;border:none;background:transparent;color:var(--text-primary);outline:none;width:100%;padding:24px 32px 8px;font-family:inherit;letter-spacing:-0.02em}
.note-meta{display:flex;align-items:center;gap:12px;padding:0 32px 8px;font-size:0.75rem;color:var(--text-tertiary)}

/* Editor */
.editor-container{flex:1;overflow-y:auto;padding:0 32px 100px}
.editor{min-height:100%;outline:none;font-size:0.95rem;line-height:1.75;color:var(--text-primary);font-family:'Inter',sans-serif}
.editor h1{font-size:1.8rem;font-weight:700;margin:24px 0 12px;letter-spacing:-0.02em}
.editor h2{font-size:1.4rem;font-weight:600;margin:20px 0 10px}
.editor h3{font-size:1.15rem;font-weight:600;margin:16px 0 8px}
.editor p{margin-bottom:8px}
.editor ul,.editor ol{padding-left:24px;margin-bottom:8px}
.editor blockquote{border-left:3px solid var(--accent);padding-left:16px;color:var(--text-secondary);margin:12px 0;font-style:italic}
.editor code{background:var(--bg-tertiary);padding:2px 6px;border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:0.85em}
.editor pre{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius);padding:16px;margin:12px 0;overflow-x:auto}
.editor pre code{background:none;padding:0}
.editor hr{border:none;border-top:2px solid var(--border);margin:20px 0}
.editor a{color:var(--accent)}

/* Empty State */
.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-tertiary);gap:16px;padding:40px;text-align:center}
.empty-state svg{width:80px;height:80px;opacity:0.3}
.empty-state h2{font-size:1.3rem;color:var(--text-secondary)}
.empty-state p{font-size:0.9rem;max-width:360px;line-height:1.6}

/* Tags Panel */
.tags-panel{padding:8 12px;border-top:1px solid var(--border);flex-shrink:0}
.tag-item{display:flex;align-items:center;gap:8px;padding:5px 10px;border-radius:6px;cursor:pointer;font-size:0.82rem;color:var(--text-secondary);transition:all var(--transition)}
.tag-item:hover{background:var(--bg-hover)}
.tag-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}

/* Modal */
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.4);z-index:1000;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity 0.2s ease}
.modal-overlay.active{opacity:1;pointer-events:auto}
.modal{background:var(--bg-primary);border-radius:16px;padding:28px;width:90%;max-width:420px;box-shadow:0 20px 60px rgba(0,0,0,0.2);transform:scale(0.95);transition:transform 0.2s ease}
.modal-overlay.active .modal{transform:scale(1)}
.modal h3{font-size:1.1rem;margin-bottom:16px}
.modal input,.modal select{width:100%;padding:10px 14px;border:1.5px solid var(--border);border-radius:8px;background:var(--bg-secondary);color:var(--text-primary);font-size:0.9rem;font-family:inherit;outline:none;margin-bottom:12px}
.modal input:focus{border-color:var(--accent)}
.modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:8px}
.btn{padding:9px 20px;border-radius:8px;border:none;font-size:0.85rem;font-weight:500;cursor:pointer;font-family:inherit;transition:all var(--transition)}
.btn-primary{background:var(--accent);color:#fff}
.btn-primary:hover{opacity:0.9}
.btn-secondary{background:var(--bg-tertiary);color:var(--text-secondary)}
.btn-secondary:hover{background:var(--bg-hover)}

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

/* Toast */
.toast-container{position:fixed;bottom:24px;right:24px;z-index:3000;display:flex;flex-direction:column;gap:8px}
.toast{padding:12px 20px;border-radius:10px;background:var(--bg-primary);border:1px solid var(--border);box-shadow:0 8px 30px rgba(0,0,0,0.15);font-size:0.85rem;color:var(--text-primary);display:flex;align-items:center;gap:10px;animation:toastIn 0.3s ease}
.toast.out{animation:toastOut 0.3s ease forwards}

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

/* Responsive */
@media(max-width:768px){
.sidebar{position:absolute;height:100%;box-shadow:var(--shadow)}
.sidebar.collapsed{transform:translateX(calc(-1 * var(--sidebar-width)))}
.note-title-input,.editor-container{padding-left:20px;padding-right:20px}
.note-meta{padding-left:20px;padding-right:20px}
.toolbar{padding:0 12px}
}

/* Word count bar */
.status-bar{height:28px;border-top:1px solid var(--border);display:flex;align-items:center;padding:0 20px;font-size:0.7rem;color:var(--text-tertiary);gap:16px;flex-shrink:0}

/* Checkbox in editor */
.editor input[type="checkbox"]{margin-right:6px;accent-color:var(--accent)}

/* Image placeholder */
.editor img{max-width:100%;border-radius:8px;margin:8px 0}

/* Print styles */
@media print{.sidebar,.toolbar,.status-bar{display:none}.main{width:100%}}
</style>
</head>
<body>
<div class="app" id="app">
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="logo">
<svg viewBox="0 0 28 28" fill="none"><rect x="3" y="3" width="22" height="22" rx="6" fill="currentColor"/><path d="M9 14l3 3 7-7" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
NoteFlow
</div>
<div class="sidebar-actions">
<button class="icon-btn" onclick="toggleTheme()" title="Toggle theme" id="themeBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><path d="M12 1v2m0 18v2M4.22 4.22l1.42 1.42m12.72 12.72l1.42 1.42M1 12h2m18 0h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
</button>
<button class="icon-btn" onclick="toggleSidebar()" title="Toggle sidebar">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/></svg>
</button>
</div>
</div>

<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" placeholder="Search notes..." id="searchInput" oninput="filterNotes()">
</div>

<div class="folders-section">
<div class="section-label">
<span>Folders</span>
<button class="icon-btn" onclick="showAddFolderModal()" style="width:24px;height:24px">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
</button>
</div>
<div id="foldersList"></div>
</div>

<div class="notes-list-section">
<div class="section-label"><span>Notes</span><span id="noteCount"></span></div>
<div class="notes-list" id="notesList"></div>
</div>

<div class="tags-panel" style="padding:8px 12px;border-top:1px solid var(--border)">
<div class="section-label"><span>Tags</span></div>
<div id="tagsList"></div>
</div>
</aside>

<!-- Main -->
<main class="main" id="mainContent">
<div class="toolbar" id="toolbar">
<button class="icon-btn" onclick="toggleSidebar()" title="Toggle sidebar">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/></svg>
</button>
<div class="toolbar-group">
<button class="tool-btn" onclick="execCmd('bold')" title="Bold"><b>B</b></button>
<button class="tool-btn" onclick="execCmd('italic')" title="Italic"><i>I</i></button>
<button class="tool-btn" onclick="execCmd('underline')" title="Underline"><u>U</u></button>
<button class="tool-btn" onclick="execCmd('strikeThrough')" title="Strikethrough"><s>S</s></button>
</div>
<div class="toolbar-group">
<select onchange="execCmd('formatBlock',this.value);this.selectedIndex=0" style="padding:6px 8px;border:1.5px solid var(--border);border-radius:6px;background:var(--bg-secondary);color:var(--text-primary);font-size:0.8rem;font-family:inherit;cursor:pointer">
<option value="">Heading</option>
<option value="h1">Heading 1</option>
<option value="h2">Heading 2</option>
<option value="h3">Heading 3</option>
<option value="p">Paragraph</option>
</select>
</div>
<div class="toolbar-group">
<button class="tool-btn" onclick="execCmd('insertUnorderedList')" title="Bullet list">
<svg 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 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"/><text x="3" y="8" font-size="7" fill="currentColor" stroke="none">1</text><text x="3" y="14" font-size="7" fill="currentColor" stroke="none">2</text><text x="3" y="20" font-size="7" fill="currentColor" stroke="none">3</text></svg>
</button>
<button class="tool-btn" onclick="execCmd('formatBlock','blockquote')" title="Quote">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></svg>
</button>
</div>
<div class="toolbar-group">
<button class="tool-btn" onclick="insertCodeBlock()" title="Code block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
</button>
<button class="tool-btn" onclick="insertHR()" title="Horizontal rule">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="12" x2="21" y2="12"/></svg>
</button>
<button class="tool-btn" onclick="insertCheckbox()" title="Checkbox">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="3"/><polyline points="9 12 11 14 15 10"/></svg>
</button>
</div>
<div class="toolbar-spacer"></div>
<button class="tool-btn" onclick="showTagModal()" title="Add tags">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.59 13.41l-7.17 7.17a2 2 0 01-2.83 0L2 12V2h10l8.59 8.59a2 2 0 010 2.82z"/><line x1="7" y1="7" x2="7.01" y2="7"/></svg>
</button>
<button class="tool-btn" onclick="showNoteContextMenu(event)" title="More options">
<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/></svg>
</button>
</div>

<div id="editorArea">
<input type="text" class="note-title-input" id="noteTitleInput" placeholder="Untitled note..." oninput="onTitleChange()">
<div class="note-meta" id="noteMeta"></div>
<div class="editor-container">
<div class="editor" contenteditable="true" id="editor" oninput="onEditorChange()" onkeydown="handleEditorKeys(event)"></div>
</div>
</div>

<div class="status-bar" id="statusBar">
<span id="wordCount">0 words</span>
<span id="charCount">0 characters</span>
<span id="lastSaved">Not saved yet</span>
</div>
</main>
</div>

<!-- Context Menu -->
<div class="context-menu" id="contextMenu">
<div class="ctx-item" onclick="duplicateNote()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
Duplicate
</div>
<div class="ctx-item" onclick="changeNoteFolder()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>
Move to folder...
</div>
<div class="ctx-item" onclick="togglePinNote()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
<span id="pinLabel">Pin note</span>
</div>
<div class="ctx-divider"></div>
<div class="ctx-item danger" onclick="deleteCurrentNote()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>
Delete note
</div>
</div>

<!-- Add 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')addFolder()">
<div class="modal-actions">
<button class="btn btn-secondary" onclick="closeModal('folderModal')">Cancel</button>
<button class="btn btn-primary" onclick="addFolder()">Create</button>
</div>
</div>
</div>

<!-- Tag Modal -->
<div class="modal-overlay" id="tagModal">
<div class="modal">
<h3>Edit Tags</h3>
<input type="text" id="newTagInput" placeholder="Add a tag..." onkeydown="if(event.key==='Enter')addTag()">
<div id="currentTagsDisplay" style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px"></div>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="closeModal('tagModal')">Done</button>
</div>
</div>
</div>

<!-- Move Note Modal -->
<div class="modal-overlay" id="moveNoteModal">
<div class="modal">
<h3>Move to Folder</h3>
<select id="moveFolderSelect"></select>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="closeModal('moveNoteModal')">Cancel</button>
<button class="btn btn-primary" onclick="doMoveNote()">Move</button>
</div>
</div>
</div>

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

<script>
// ===== DATA =====
const DEFAULT_FOLDERS = [
  { id: 'all', name: 'All Notes', icon: 'notes', system: true },
  { id: 'personal', name: 'Personal', icon: 'user' },
  { id: 'work', name: 'Work', icon: 'briefcase' },
  { id: 'ideas', name: 'Ideas', icon: 'bulb' },
  { id: 'journal', name: 'Journal', icon: 'book' }
];

const DEFAULT_NOTES = [
  {
    id: '1', title: 'Welcome to NoteFlow ✨', folderId: 'personal', pinned: true,
    content: '<h1>Welcome to NoteFlow!</h1><p>This is your new 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>Add tags to categorize your notes</li><li>Search through all your notes instantly</li><li>Toggle dark mode for comfortable reading</li></ul><blockquote>NoteFlow saves everything automatically to your browser\'s local storage.</blockquote><h2>Getting Started</h2><p>Select a folder from the sidebar, then click on any note or create a new one. Your changes are saved as you type!</p><hr><p><strong>Tip:</strong> Try using keyboard shortcuts like <code>Ctrl+B</code> for bold, <code>Ctrl+I</code> for italic.</p>',
    tags: ['welcome', 'getting-started'], created: Date.now() - 86400000 * 2, updated: Date.now() - 3600000
  },
  {
    id: '2', title: 'Project Ideas Q1 2025', folderId: 'ideas', pinned: false,
    content: '<h2>App Ideas</h2><ol><li>Habit tracker with streak visualization</li><li>Recipe organizer with meal planning</li><li>Pomodoro timer with ambient sounds</li></ol><h2>Features to explore</h2><ul><li>AI-powered note summarization</li><li>Cross-device sync via WebDAV</li><li>Markdown export support</li></ul>',
    tags: ['ideas', 'planning'], created: Date.now() - 86400000, updated: Date.now() - 7200000
  },
  {
    id: '3', title: 'Meeting Notes — Design Review', folderId: 'work', pinned: false,
    content: '<h2>Attendees</h2><p>Sarah, Mike, Alex, Jordan</p><h2>Key Decisions</h2><ul><li>Switch to a card-based layout for the dashboard</li><li>Implement dark mode by end of sprint</li><li>Reduce color palette to 5 primary colors</li></ul><h2>Action Items</h2><p>☐ Sarah: Create wireframes by Friday<br>☑ Mike: Set up design tokens<br>☐ Alex: Review accessibility compliance</p>',
    tags: ['work', 'meetings'], created: Date.now() - 86400000 * 3, updated: Date.now() - 86400000
  },
  {
    id: '4', title: 'Weekly Journal — Week 12', folderId: 'journal', pinned: false,
    content: '<h2>Monday</h2><p>Started the week with a productive morning routine. Felt energized and focused.</p><h2>Wednesday</h2><p>Had an interesting conversation about design systems at lunch. Need to read more about atomic design principles.</p><h2>Friday</h2><p>Wrapped up the sprint ahead of schedule. Time for some well-deserved rest!</p>',
    tags: ['journal', 'weekly'], created: Date.now() - 86400000 * 5, updated: Date.now() - 86400000 * 2
  }
];

let state = {
  folders: [],
  notes: [],
  activeFolderId: 'all',
  activeNoteId: null,
  searchQuery: '',
  theme: 'light'
};

// ===== INIT =====
function init() {
  loadState();
  if (!state.folders.length) state.folders = JSON.parse(JSON.stringify(DEFAULT_FOLDERS));
  if (!state.notes.length) state.notes = JSON.parse(JSON.stringify(DEFAULT_NOTES));
  
  document.documentElement.setAttribute('data-theme', state.theme);
  renderFolders();
  renderNotesList();
  renderTags();
  updateNoteCount();
  
  if (state.activeNoteId) {
    selectNote(state.activeNoteId);
  } else {
    showEmptyState();
  }
}

function loadState() {
  try {
    const saved = localStorage.getItem('noteflow_state');
    if (saved) {
      const parsed = JSON.parse(saved);
      state.folders = parsed.folders || [];
      state.notes = parsed.notes || [];
      state.activeFolderId = parsed.activeFolderId || 'all';
      state.activeNoteId = parsed.activeNoteId || null;
      state.theme = parsed.theme || 'light';
    }
  } catch(e) { console.warn('Failed to load state', e); }
}

function saveState() {
  try {
    localStorage.setItem('noteflow_state', JSON.stringify({
      folders: state.folders,
      notes: state.notes,
      activeFolderId: state.activeFolderId,
      activeNoteId: state.activeNoteId,
      theme: state.theme
    }));
  } catch(e) { console.warn('Failed to save state', e); }
}

// ===== RENDERING =====
function renderFolders() {
  const container = document.getElementById('foldersList');
  container.innerHTML = state.folders.map(f => {
    if (f.system) return '';
    const count = state.notes.filter(n => n.folderId === f.id).length;
    const icons = { user: '👤', briefcase: '💼', bulb: '💡', book: '📖', default: '📁' };
    const icon = icons[f.icon] || icons.default;
    return `<div class="folder-item ${state.activeFolderId === f.id ? 'active' : ''}" onclick="selectFolder('${f.id}')">
      <span>${icon}</span>
      <span>${escHtml(f.name)}</span>
      <span class="count">${count}</span>
    </div>`;
  }).join('');
}

function renderNotesList() {
  const container = document.getElementById('notesList');
  let notes = getFilteredNotes();
  
  // Sort: pinned first, then by updated date
  notes.sort((a, b) => {
    if (a.pinned && !b.pinned) return -1;
    if (!a.pinned && b.pinned) return 1;
    return b.updated - a.updated;
  });

  container.innerHTML = notes.map(n => {
    const preview = stripHtml(n.content).substring(0, 80);
    const dateStr = formatDate(n.updated);
    const tagsHtml = n.tags.slice(0, 3).map((t, i) => 
      `<span class="mini-tag" style="background:var(--tag-colors,${getTagColor(t)});color:#fff">${escHtml(t)}</span>`
    ).join('');
    
    return `<div class="note-item ${state.activeNoteId === n.id ? 'active' : ''}" onclick="selectNote('${n.id}')" oncontextmenu="showNoteItemContextMenu(event,'${n.id}')">
      <div class="note-item-title">${n.pinned ? '📌 ' : ''}${escHtml(n.title || 'Untitled')}</div>
      <div class="note-item-preview">${escHtml(preview)}</div>
      ${tagsHtml ? `<div class="note-item-tags">${tagsHtml}</div>` : ''}
      <div class="note-item-date">${dateStr}</div>
    </div>`;
  }).join('');

  if (!notes.length) {
    container.innerHTML = '<div style="text-align:center;padding:40px 20px;color:var(--text-tertiary);font-size:0.85rem">No notes found</div>';
  }
}

function renderTags() {
  const container = document.getElementById('tagsList');
  const allTags = {};
  state.notes.forEach(n => n.tags.forEach(t => { allTags[t] = (allTags[t] || 0) + 1; }));
  
  container.innerHTML = Object.entries(allTags).map(([tag, count]) => 
    `<div class="tag-item" onclick="filterByTag('${escHtml(tag)}')">
      <span class="tag-dot" style="background:${getTagColor(tag)}"></span>
      <span>${escHtml(tag)}</span>
      <span style="margin-left:auto;font-size:0.7rem;color:var(--text-tertiary)">${count}</span>
    </div>`
  ).join('');
}

function updateNoteCount() {
  const count = getFilteredNotes().length;
  document.getElementById('noteCount').textContent = `${count} note${count !== 1 ? 's' : ''}`;
}

// ===== NOTE OPERATIONS =====
function getFilteredNotes() {
  let notes = state.notes;
  if (state.activeFolderId !== 'all') {
    notes = notes.filter(n => n.folderId === state.activeFolderId);
  }
  if (state.searchQuery) {
    const q = state.searchQuery.toLowerCase();
    notes = notes.filter(n => 
      (n.title || '').toLowerCase().includes(q) || 
      stripHtml(n.content).toLowerCase().includes(q) ||
      n.tags.some(t => t.toLowerCase().includes(q))
    );
  }
  return notes;
}

function selectNote(id) {
  state.activeNoteId = id;
  const note = state.notes.find(n => n.id === id);
  if (!note) { showEmptyState(); return; }
  
  document.getElementById('noteTitleInput').value = note.title || '';
  document.getElementById('editor').innerHTML = note.content || '';
  updateNoteMeta(note);
  updateWordCount();
  renderNotesList();
  saveState();
}

function selectFolder(id) {
  state.activeFolderId = id;
  state.searchQuery = '';
  document.getElementById('searchInput').value = '';
  renderFolders();
  renderNotesList();
  updateNoteCount();
  saveState();
}

function createNewNote() {
  const folderId = state.activeFolderId === 'all' ? (state.folders[1]?.id || 'personal') : state.activeFolderId;
  const note = {
    id: generateId(),
    title: '',
    content: '',
    folderId,
    tags: [],
    pinned: false,
    created: Date.now(),
    updated: Date.now()
  };
  state.notes.unshift(note);
  selectNote(note.id);
  document.getElementById('noteTitleInput').focus();
  saveState();
  renderFolders();
}

function deleteCurrentNote() {
  if (!state.activeNoteId) return;
  hideContextMenu();
  state.notes = state.notes.filter(n => n.id !== state.activeNoteId);
  state.activeNoteId = null;
  showEmptyState();
  renderNotesList();
  renderFolders();
  updateNoteCount();
  saveState();
  showToast('Note deleted');
}

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

function togglePinNote() {
  hideContextMenu();
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.pinned = !note.pinned;
  document.getElementById('pinLabel').textContent = note.pinned ? 'Unpin note' : 'Pin note';
  renderNotesList();
  saveState();
  showToast(note.pinned ? 'Note pinned' : 'Note unpinned');
}

function changeNoteFolder() {
  hideContextMenu();
  const select = document.getElementById('moveFolderSelect');
  select.innerHTML = state.folders.filter(f => !f.system).map(f => 
    `<option value="${f.id}" ${f.id === (state.notes.find(n=>n.id===state.activeNoteId)?.folderId||'') ? 'selected' : ''}>${escHtml(f.name)}</option>`
  ).join('');
  openModal('moveNoteModal');
}

function doMoveNote() {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.folderId = document.getElementById('moveFolderSelect').value;
  closeModal('moveNoteModal');
  renderNotesList();
  renderFolders();
  saveState();
  showToast('Note moved');
}

// ===== EDITOR =====
function onTitleChange() {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.title = document.getElementById('noteTitleInput').value;
  note.updated = Date.now();
  renderNotesList();
  debouncedSave();
}

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

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

function execCmd(cmd, val = null) {
  document.execCommand(cmd, false, val);
  document.getElementById('editor').focus();
}

function insertCodeBlock() {
  const sel = window.getSelection();
  if (sel.rangeCount) {
    const text = sel.toString() || 'code here';
    document.execCommand('insertHTML', false, `<pre><code>${escHtml(text)}</code></pre><p></p>`);
  }
}

function insertHR() {
  document.execCommand('insertHTML', false, '<hr>');
}

function insertCheckbox() {
  const checked = Math.random() > 0.5 ? 'checked' : '';
  document.execCommand('insertHTML', false, `<div><input type="checkbox" ${checked}> Task item</div>`);
}

// ===== FOLDER OPERATIONS =====
function showAddFolderModal() { openModal('folderModal'); setTimeout(() => document.getElementById('newFolderName').focus(), 100); }

function addFolder() {
  const name = document.getElementById('newFolderName').value.trim();
  if (!name) return;
  state.folders.push({ id: generateId(), name, icon: 'default' });
  document.getElementById('newFolderName').value = '';
  closeModal('folderModal');
  renderFolders();
  saveState();
  showToast(`Folder "${name}" created`);
}

// ===== TAG OPERATIONS =====
function showTagModal() {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  renderCurrentTags(note);
  openModal('tagModal');
  setTimeout(() => document.getElementById('newTagInput').focus(), 100);
}

function renderCurrentTags(note) {
  const container = document.getElementById('currentTagsDisplay');
  container.innerHTML = note.tags.map(t => 
    `<span class="mini-tag" style="background:${getTagColor(t)};color:#fff;padding:4px 10px;border-radius:6px;display:flex;align-items:center;gap:6px">
      ${escHtml(t)}
      <span onclick="removeTag('${escHtml(t)}')" style="cursor:pointer;font-weight:bold">×</span>
    </span>`
  ).join('');
}

function addTag() {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  const tag = document.getElementById('newTagInput').value.trim().toLowerCase();
  if (!tag || note.tags.includes(tag)) { document.getElementById('newTagInput').value = ''; return; }
  note.tags.push(tag);
  document.getElementById('newTagInput').value = '';
  renderCurrentTags(note);
  renderTags();
  saveState();
}

function removeTag(tag) {
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (!note) return;
  note.tags = note.tags.filter(t => t !== tag);
  renderCurrentTags(note);
  renderTags();
  saveState();
}

function filterByTag(tag) {
  state.searchQuery = tag;
  document.getElementById('searchInput').value = tag;
  renderNotesList();
  updateNoteCount();
}

// ===== SEARCH =====
function filterNotes() {
  state.searchQuery = document.getElementById('searchInput').value.trim();
  renderNotesList();
  updateNoteCount();
}

// ===== UI HELPERS =====
function showEmptyState() {
  const editorArea = document.getElementById('editorArea');
  editorArea.innerHTML = `
    <div class="empty-state">
      <svg viewBox="0 0 80 80" fill="none" stroke="currentColor" stroke-width="1.5">
        <rect x="12" y="8" width="56" height="64" rx="8"/>
        <line x1="22" y1="24" x2="58" y2="24"/><line x1="22" y1="34" x2="50" y2="34"/>
        <line x1="22" y1="44" x2="54" y2="44"/><line x1="22" y1="54" x2="42" y2="54"/>
      </svg>
      <h2>No note selected</h2>
      <p>Select a note from the sidebar or create a new one to get started.</p>
      <button class="btn btn-primary" onclick="createNewNote()" style="margin-top:8px">+ New Note</button>
    </div>`;
  document.getElementById('statusBar').style.display = 'none';
}

function updateNoteMeta(note) {
  const meta = document.getElementById('noteMeta');
  const folder = state.folders.find(f => f.id === note.folderId);
  meta.innerHTML = `
    <span>📁 ${escHtml(folder?.name || 'Unknown')}</span>
    <span>Created: ${formatDate(note.created)}</span>
    <span>Modified: ${formatDate(note.updated)}</span>
  `;
}

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

function updateLastSaved() {
  document.getElementById('lastSaved').textContent = `Saved ${formatTime(Date.now())}`;
}

// ===== CONTEXT MENU =====
function showNoteContextMenu(e) {
  e.preventDefault();
  const note = state.notes.find(n => n.id === state.activeNoteId);
  if (note) document.getElementById('pinLabel').textContent = note.pinned ? 'Unpin note' : 'Pin note';
  showContextMenuAt(e.clientX, e.clientY);
}

function showNoteItemContextMenu(e, id) {
  e.preventDefault();
  e.stopPropagation();
  selectNote(id);
  const note = state.notes.find(n => n.id === id);
  if (note) document.getElementById('pinLabel').textContent = note.pinned ? 'Unpin note' : 'Pin label';
  showContextMenuAt(e.clientX, e.clientY);
}

function showContextMenuAt(x, y) {
  const menu = document.getElementById('contextMenu');
  menu.style.left = Math.min(x, window.innerWidth - 200) + 'px';
  menu.style.top = Math.min(y, window.innerHeight - 250) + 'px';
  menu.classList.add('show');
}

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

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

// ===== MODALS =====
function openModal(id) { document.getElementById(id).classList.add('active'); }
function closeModal(id) { document.getElementById(id).classList.remove('active'); }

document.querySelectorAll('.modal-overlay').forEach(overlay => {
  overlay.addEventListener('click', e => { if (e.target === overlay) overlay.classList.remove('active'); });
});

// ===== THEME =====
function toggleTheme() {
  state.theme = state.theme === 'light' ? 'dark' : 'light';
  document.documentElement.setAttribute('data-theme', state.theme);
  saveState();
}

// ===== SIDEBAR =====
function toggleSidebar() {
  document.getElementById('sidebar').classList.toggle('collapsed');
}

// ===== TOAST =====
function showToast(msg) {
  const container = document.getElementById('toastContainer');
  const toast = document.createElement('div');
  toast.className = 'toast';
  toast.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg> ${escHtml(msg)}`;
  container.appendChild(toast);
  setTimeout(() => { toast.classList.add('out'); setTimeout(() => toast.remove(), 300); }, 2500);
}

// ===== UTILITIES =====
function generateId() { return Date.now().toString(36) + Math.random().toString(36).substr(2, 5); }
function escHtml(str) { const d = document.createElement('div'); d.textContent = str; 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', year: d.getFullYear() !== now.getFullYear() ? 'numeric' : undefined });
}

function formatTime(ts) {
  return new Date(ts).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
}

function getTagColor(tag) {
  let hash = 0;
  for (let i = 0; i < tag.length; i++) hash = tag.charCodeAt(i) + ((hash << 5) - hash);
  const colors = ['#6c5ce7','#00b894','#e17055','#0984e3','#fdcb6e','#d63031','#e84393','#00cec9'];
  return colors[Math.abs(hash) % colors.length];
}

let saveTimeout;
function debouncedSave() {
  clearTimeout(saveTimeout);
  saveTimeout = setTimeout(() => { saveState(); updateLastSaved(); }, 500);
}

// ===== KEYBOARD SHORTCUTS =====
document.addEventListener('keydown', e => {
  if ((e.ctrlKey || e.metaKey) && e.key === 'n') { e.preventDefault(); createNewNote(); }
  if ((e.ctrlKey || e.metaKey) && e.key === 'f') { e.preventDefault(); document.getElementById('searchInput').focus(); }
  if (e.key === 'Escape') {
    document.querySelectorAll('.modal-overlay.active').forEach(m => m.classList.remove('active'));
    hideContextMenu();
  }
});

// ===== INIT =====
init();
</script>
</body>
</html>
<!-- agent-meta {"model":"thinkingcap-qwen3.6-27b","provider":"lmstudio","persona":"basic","sessionId":"a9f699e4-f3d8-4838-8d8d-ec95f038a53f","tokensIn":217,"tokensOut":13070,"tokensTotal":13287,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-07-19T01:42:32.694Z"} -->