← Notes results

Notes

Qwen 3.6 27b · typescript

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 108,555 bytes · SHA-256 1acdcd57512e
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Notes — Your Personal Workspace</title>
<style>
/* ── css: reset ── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  height: 100%;
  overflow: hidden;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

input, button, textarea {
  font-family: inherit;
  font-size: inherit;
}

button {
  cursor: pointer;
  border: none;
  background: none;
  outline: none;
}

input {
  outline: none;
  border: none;
  background: none;
}

::selection {
  background: rgba(108, 92, 231, 0.25);
}

::-webkit-scrollbar {
  width: 6px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: rgba(0,0,0,0.12);
  border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
  background: rgba(0,0,0,0.2);
}
/* ── css: layout ── */
#app {
  display: flex;
  height: 100vh;
  width: 100vw;
  overflow: hidden;
}

/* Sidebar */
#sidebar {
  width: 240px;
  min-width: 160px;
  max-width: 320px;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--bg-secondary);
  transition: width 0.25s ease, min-width 0.25s ease;
  overflow: hidden;
}

#sidebar.collapsed {
  width: 0 !important;
  min-width: 0 !important;
  border-right: none;
}

#sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 16px 8px;
  flex-shrink: 0;
}

#app-logo {
  font-size: 20px;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: -0.5px;
}

#sidebar-actions {
  padding: 4px 16px;
  flex-shrink: 0;
}

#sidebar-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 8px;
}

#sidebar-footer {
  padding: 8px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

/* Resize handles */
.resize-handle {
  width: 4px;
  cursor: col-resize;
  background: transparent;
  transition: background 0.15s;
  flex-shrink: 0;
  position: relative;
}
.resize-handle:hover,
.resize-handle.active {
  background: var(--accent);
}

/* Note list pane */
#note-list-pane {
  width: 300px;
  min-width: 200px;
  max-width: 450px;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--bg);
  overflow: hidden;
}

#note-list-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

#note-list-title-area {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

#note-list-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}

#note-list-count {
  font-size: 12px;
  color: var(--text-tertiary);
}

#note-list-actions {
  display: flex;
  gap: 2px;
}

#search-area {
  padding: 8px 12px;
  flex-shrink: 0;
}

#search-input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  background: var(--search-bg);
  color: var(--text);
  font-size: 13px;
  border: 1px solid transparent;
  transition: border-color 0.15s, box-shadow 0.15s;
}
#search-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
#search-input::placeholder {
  color: var(--text-tertiary);
}

#note-list-container {
  flex: 1;
  overflow-y: auto;
  padding: 4px 8px;
}

/* Editor pane */
#editor-pane {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--bg-editor);
  min-width: 0;
  overflow: hidden;
}

#editor-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 24px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

#editor-title-area {
  flex: 1;
  min-width: 0;
}

#editor-title {
  width: 100%;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  background: transparent;
  padding: 4px 0;
}
#editor-title:focus {
  outline: none;
}
#editor-title::placeholder {
  color: var(--text-tertiary);
}

#editor-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

#saved-indicator {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-right: 8px;
  opacity: 0;
  transition: opacity 0.3s;
}
#saved-indicator.show {
  opacity: 1;
  color: var(--success);
}

#editor-toolbar {
  display: flex;
  align-items: center;
  padding: 6px 24px;
  border-bottom: 1px solid var(--border);
  gap: 2px;
  flex-shrink: 0;
  background: var(--toolbar-bg);
  overflow-x: auto;
}

.toolbar-btn {
  padding: 6px 8px;
  border-radius: 4px;
  font-size: 13px;
  color: var(--text-secondary);
  transition: background 0.1s, color 0.1s;
  min-width: 30px;
  text-align: center;
}
.toolbar-btn:hover {
  background: var(--toolbar-hover);
  color: var(--text);
}
.toolbar-btn.active {
  background: var(--toolbar-active);
  color: var(--accent);
}

.toolbar-sep {
  width: 1px;
  height: 20px;
  background: var(--toolbar-border);
  margin: 0 4px;
  flex-shrink: 0;
}

#editor-body {
  flex: 1;
  overflow-y: auto;
  padding: 24px 48px;
}

#editor-content {
  max-width: 720px;
  margin: 0 auto;
  font-family: 'Georgia', 'Noto Serif', 'Source Serif Pro', serif;
  font-size: 16px;
  line-height: 1.7;
  color: var(--text);
  outline: none;
  min-height: 100%;
}

#editor-content:empty::before {
  content: 'Start writing…';
  color: var(--text-tertiary);
  pointer-events: none;
}

#editor-tags-area {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 48px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  flex-wrap: wrap;
}

#tags-container {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: 12px;
  font-size: 12px;
  background: var(--tag-bg);
  color: var(--tag-text);
  cursor: pointer;
  transition: background 0.1s;
}
.tag-chip:hover {
  background: var(--bg-hover);
}

#tag-input {
  font-size: 12px;
  color: var(--text);
  min-width: 80px;
  padding: 4px 8px;
}
#tag-input::placeholder {
  color: var(--text-tertiary);
}

/* Empty state */
#editor-empty-state {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.empty-state-content {
  text-align: center;
  color: var(--text-tertiary);
}

.empty-state-icon {
  font-size: 48px;
  margin-bottom: 16px;
}

.empty-state-text {
  font-size: 16px;
}

/* Icon buttons */
.icon-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  font-size: 16px;
  color: var(--text-secondary);
  transition: background 0.1s, color 0.1s;
}
.icon-btn:hover {
  background: var(--bg-hover);
  color: var(--text);
}
.icon-btn.active {
  color: var(--accent);
  background: var(--accent-bg);
}

/* Dropdowns */
.dropdown {
  position: fixed;
  background: var(--bg-editor);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px var(--shadow-medium);
  padding: 4px;
  z-index: 1000;
  min-width: 160px;
}

.dropdown-item {
  padding: 8px 12px;
  font-size: 13px;
  color: var(--text);
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.1s;
}
.dropdown-item:hover {
  background: var(--bg-hover);
}
.dropdown-item.danger {
  color: var(--danger);
}
.dropdown-item.danger:hover {
  background: var(--danger-bg);
}

/* Modal */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
}

.modal-content {
  background: var(--bg-editor);
  border-radius: 12px;
  padding: 24px;
  min-width: 320px;
  box-shadow: 0 16px 48px var(--shadow-medium);
}

.modal-content h3 {
  margin-bottom: 16px;
  font-size: 16px;
  color: var(--text);
}

.modal-content input {
  width: 100%;
  padding: 10px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  margin-bottom: 16px;
}
.modal-content input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}

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

.btn-primary {
  padding: 8px 20px;
  border-radius: 6px;
  background: var(--accent);
  color: white;
  font-size: 13px;
  font-weight: 500;
  transition: opacity 0.1s;
}
.btn-primary:hover {
  opacity: 0.9;
}

.btn-secondary {
  padding: 8px 20px;
  border-radius: 6px;
  background: var(--bg-tertiary);
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  transition: background 0.1s;
}
.btn-secondary:hover {
  background: var(--bg-hover);
}
/* ── css: sidebar ── */
/* Sidebar items */
.sidebar-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.1s;
  margin-bottom: 2px;
  position: relative;
  user-select: none;
}
.sidebar-item:hover {
  background: var(--bg-hover);
}
.sidebar-item.active {
  background: var(--accent-bg);
  color: var(--accent);
}

.sidebar-item-icon {
  font-size: 16px;
  flex-shrink: 0;
  width: 20px;
  text-align: center;
}

.sidebar-item-name {
  flex: 1;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-item-count {
  font-size: 11px;
  color: var(--text-tertiary);
  flex-shrink: 0;
}

.sidebar-item-actions {
  display: none;
  gap: 2px;
  flex-shrink: 0;
}
.sidebar-item:hover .sidebar-item-actions {
  display: flex;
}

.sidebar-item-action {
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  font-size: 12px;
  color: var(--text-tertiary);
  transition: background 0.1s, color 0.1s;
}
.sidebar-item-action:hover {
  background: var(--bg-tertiary);
  color: var(--text);
}

.sidebar-item-action.delete:hover {
  color: var(--danger);
  background: var(--danger-bg);
}

/* Notebook color dot */
.notebook-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* Collapsible notebook header */
.notebook-header {
  cursor: pointer;
}
.notebook-header .sidebar-item-name {
  font-weight: 600;
}

.notebook-notes {
  padding-left: 12px;
  overflow: hidden;
  transition: max-height 0.2s ease;
}
.notebook-notes.collapsed {
  max-height: 0 !important;
}

/* Note items in sidebar */
.sidebar-note-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.1s;
  margin-bottom: 1px;
  font-size: 12px;
  color: var(--text-secondary);
}
.sidebar-note-item:hover {
  background: var(--bg-hover);
}
.sidebar-note-item.active {
  background: var(--accent-bg);
  color: var(--accent);
}

/* Note list items */
.note-item {
  padding: 12px 16px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.1s, box-shadow 0.1s;
  margin-bottom: 4px;
  border: 1px solid transparent;
  position: relative;
}
.note-item:hover {
  background: var(--bg-secondary);
}
.note-item.active {
  background: var(--accent-bg);
  border-color: var(--accent);
  box-shadow: 0 2px 8px var(--accent-bg);
}

.note-item-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.note-item-snippet {
  font-size: 12px;
  color: var(--text-tertiary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: 6px;
}

.note-item-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-tertiary);
}

.note-item-tags {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.note-item-tag {
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--tag-bg);
  color: var(--tag-text);
  font-size: 10px;
  cursor: pointer;
  transition: background 0.1s;
}
.note-item-tag:hover {
  background: var(--bg-hover);
}

.pin-indicator {
  font-size: 10px;
  color: var(--accent);
}

/* Search highlight */
.search-highlight {
  background: var(--highlight-bg);
  border-radius: 2px;
  padding: 0 1px;
}

/* Editor content styles */
#editor-content h1 {
  font-size: 28px;
  font-weight: 700;
  margin: 24px 0 12px;
  line-height: 1.3;
}
#editor-content h2 {
  font-size: 22px;
  font-weight: 600;
  margin: 20px 0 10px;
  line-height: 1.35;
}
#editor-content h3 {
  font-size: 18px;
  font-weight: 600;
  margin: 16px 0 8px;
  line-height: 1.4;
}
#editor-content p {
  margin: 8px 0;
}
#editor-content blockquote {
  border-left: 3px solid var(--accent);
  padding-left: 16px;
  margin: 12px 0;
  color: var(--text-secondary);
  font-style: italic;
}
#editor-content code {
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  background: var(--bg-tertiary);
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 0.9em;
}
#editor-content hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 20px 0;
}
#editor-content ul, #editor-content ol {
  padding-left: 24px;
  margin: 8px 0;
}
#editor-content li {
  margin: 4px 0;
}

/* Checklist items */
.checklist-item {
  list-style: none;
  margin-left: -20px;
  padding-left: 28px;
  position: relative;
}
.checklist-checkbox {
  position: absolute;
  left: 0;
  top: 3px;
  width: 16px;
  height: 16px;
  border: 2px solid var(--checkbox-unchecked);
  border-radius: 3px;
  cursor: pointer;
  transition: all 0.15s;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.checklist-checkbox.checked {
  background: var(--checkbox-checked);
  border-color: var(--checkbox-checked);
}
.checklist-checkbox.checked::after {
  content: '✓';
  color: white;
  font-size: 10px;
  font-weight: bold;
}
.checklist-item.checked-text {
  text-decoration: line-through;
  color: var(--text-tertiary);
}

/* Sidebar collapse button */
#btn-collapse-sidebar {
  width: 100%;
  height: 32px;
  border-radius: 6px;
  font-size: 12px;
  color: var(--text-tertiary);
  transition: background 0.1s, color 0.1s;
}
#btn-collapse-sidebar:hover {
  background: var(--bg-hover);
  color: var(--text);
}

/* Sidebar expand button (shown when collapsed) */
#sidebar-expand {
  position: fixed;
  top: 12px;
  left: 8px;
  width: 32px;
  height: 32px;
  display: none;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  cursor: pointer;
  z-index: 100;
  font-size: 16px;
  transition: background 0.1s;
}
#sidebar-expand:hover {
  background: var(--bg-hover);
}

/* Drop target highlight */
.drop-target {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: 8px;
}

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

.dropdown {
  animation: fadeIn 0.12s ease;
}
/* ── css: theme ── */
/* Light theme (default) */
:root {
  --bg: #FAFAF9;
  --bg-secondary: #F3F2F0;
  --bg-tertiary: #EBEAE7;
  --bg-hover: #E8E7E4;
  --bg-active: #DFDED9;
  --bg-editor: #FFFFFF;
  --text: #2C2C2C;
  --text-secondary: #6B6B6B;
  --text-tertiary: #9A9A9A;
  --border: #E5E4E1;
  --border-light: #EDECE9;
  --accent: #6C5CE7;
  --accent-light: #A29BFE;
  --accent-bg: rgba(108, 92, 231, 0.08);
  --danger: #E74C3C;
  --danger-bg: rgba(231, 76, 60, 0.08);
  --success: #27AE60;
  --success-bg: rgba(39, 174, 96, 0.08);
  --shadow: rgba(0, 0, 0, 0.06);
  --shadow-medium: rgba(0, 0, 0, 0.1);
  --tag-bg: #E8E7E4;
  --tag-text: #5A5A5A;
  --checkbox-unchecked: #C4C4C4;
  --checkbox-checked: #6C5CE7;
  --toolbar-bg: #FFFFFF;
  --toolbar-border: #E5E4E1;
  --toolbar-hover: #F3F2F0;
  --toolbar-active: #DFDED9;
  --search-bg: #F3F2F0;
  --highlight-bg: rgba(255, 235, 59, 0.3);
}

/* Dark theme */
body.dark-mode {
  --bg: #1A1A1A;
  --bg-secondary: #232323;
  --bg-tertiary: #2A2A2A;
  --bg-hover: #333333;
  --bg-active: #3A3A3A;
  --bg-editor: #1E1E1E;
  --text: #E8E8E8;
  --text-secondary: #A0A0A0;
  --text-tertiary: #6B6B6B;
  --border: #333333;
  --border-light: #2A2A2A;
  --accent: #A29BFE;
  --accent-light: #6C5CE7;
  --accent-bg: rgba(162, 155, 254, 0.12);
  --danger: #FF6B6B;
  --danger-bg: rgba(255, 107, 107, 0.12);
  --success: #69DB7C;
  --success-bg: rgba(105, 219, 124, 0.12);
  --shadow: rgba(0, 0, 0, 0.3);
  --shadow-medium: rgba(0, 0, 0, 0.5);
  --tag-bg: #333333;
  --tag-text: #B0B0B0;
  --checkbox-unchecked: #555555;
  --checkbox-checked: #A29BFE;
  --toolbar-bg: #232323;
  --toolbar-border: #333333;
  --toolbar-hover: #2A2A2A;
  --toolbar-active: #3A3A3A;
  --search-bg: #2A2A2A;
  --highlight-bg: rgba(255, 235, 59, 0.2);
}

body.dark-mode ::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.1);
}
body.dark-mode ::-webkit-scrollbar-thumb:hover {
  background: rgba(255,255,255,0.2);
}
</style>
</head>
<body>
<div id="app">
  <!-- Sidebar -->
  <div id="sidebar">
    <div id="sidebar-header">
      <div id="app-logo">Notes</div>
      <button id="btn-dark-mode" class="icon-btn" title="Toggle dark mode">◐</button>
    </div>
    <div id="sidebar-actions">
      <button id="btn-new-notebook" class="icon-btn" title="New Notebook">+</button>
    </div>
    <div id="sidebar-list">
      <div id="sidebar-virtual-items"></div>
      <div id="sidebar-notebooks"></div>
    </div>
    <div id="sidebar-footer">
      <button id="btn-collapse-sidebar" class="icon-btn" title="Toggle sidebar">◀</button>
    </div>
  </div>
  
  <!-- Resize handle 1 -->
  <div id="handle1" class="resize-handle"></div>
  
  <!-- Note list -->
  <div id="note-list-pane">
    <div id="note-list-header">
      <div id="note-list-title-area">
        <span id="note-list-title">All Notes</span>
        <span id="note-list-count"></span>
      </div>
      <div id="note-list-actions">
        <button id="btn-sort" class="icon-btn" title="Sort order">⇅</button>
        <button id="btn-new-note" class="icon-btn" title="New note">+</button>
      </div>
    </div>
    <div id="search-area">
      <input id="search-input" type="text" placeholder="Search notes…">
    </div>
    <div id="note-list-container"></div>
  </div>
  
  <!-- Resize handle 2 -->
  <div id="handle2" class="resize-handle"></div>
  
  <!-- Editor -->
  <div id="editor-pane">
    <div id="editor-header">
      <div id="editor-title-area">
        <input id="editor-title" type="text" placeholder="Note title">
      </div>
      <div id="editor-actions">
        <span id="saved-indicator">Saved</span>
        <button id="btn-move-note" class="icon-btn" title="Move note">↗</button>
        <button id="btn-duplicate-note" class="icon-btn" title="Duplicate">⧉</button>
        <button id="btn-pin-note" class="icon-btn" title="Pin">📌</button>
        <button id="btn-delete-note" class="icon-btn" title="Delete">🗑</button>
      </div>
    </div>
    <div id="editor-toolbar">
      <button data-cmd="bold" class="toolbar-btn" title="Bold (Ctrl+B)"><b>B</b></button>
      <button data-cmd="italic" class="toolbar-btn" title="Italic (Ctrl+I)"><i>I</i></button>
      <button data-cmd="underline" class="toolbar-btn" title="Underline (Ctrl+U)"><u>U</u></button>
      <button data-cmd="strikethrough" class="toolbar-btn" title="Strikethrough"><s>S</s></button>
      <button data-cmd="code" class="toolbar-btn" title="Inline Code" style="font-family: monospace;">&lt;&gt;</button>
      <span class="toolbar-sep"></span>
      <button data-cmd="h1" class="toolbar-btn" title="Heading 1">H1</button>
      <button data-cmd="h2" class="toolbar-btn" title="Heading 2">H2</button>
      <button data-cmd="h3" class="toolbar-btn" title="Heading 3">H3</button>
      <span class="toolbar-sep"></span>
      <button data-cmd="ul" class="toolbar-btn" title="Bullet List">•≡</button>
      <button data-cmd="ol" class="toolbar-btn" title="Numbered List">1≡</button>
      <button data-cmd="checklist" class="toolbar-btn" title="Checklist">☑</button>
      <button data-cmd="blockquote" class="toolbar-btn" title="Quote">❝</button>
      <button data-cmd="hr" class="toolbar-btn" title="Horizontal Rule">—</button>
      <span class="toolbar-sep"></span>
      <button data-cmd="undo" class="toolbar-btn" title="Undo (Ctrl+Z)">↶</button>
      <button data-cmd="redo" class="toolbar-btn" title="Redo (Ctrl+Shift+Z)">↷</button>
    </div>
    <div id="editor-body">
      <div id="editor-content" contenteditable="true" spellcheck="true"></div>
    </div>
    <div id="editor-tags-area">
      <div id="tags-container"></div>
      <input id="tag-input" type="text" placeholder="Add tag…">
    </div>
    <div id="editor-empty-state" style="display:none;">
      <div class="empty-state-content">
        <div class="empty-state-icon">📝</div>
        <div class="empty-state-text">Select or create a note</div>
      </div>
    </div>
  </div>
  
  <!-- Move note dropdown -->
  <div id="move-dropdown" class="dropdown" style="display:none;"></div>
  
  <!-- Sort dropdown -->
  <div id="sort-dropdown" class="dropdown" style="display:none;">
    <div class="dropdown-item" data-sort="modified">Last Modified</div>
    <div class="dropdown-item" data-sort="created">Date Created</div>
    <div class="dropdown-item" data-sort="alphabetical">Alphabetical</div>
  </div>
  
  <!-- Context menu for trash actions -->
  <div id="trash-menu" class="dropdown" style="display:none;">
    <div class="dropdown-item" data-action="restore">Restore</div>
    <div class="dropdown-item danger" data-action="permanent-delete">Delete Permanently</div>
  </div>
  
  <!-- New notebook dialog -->
  <div id="new-notebook-dialog" class="modal-overlay" style="display:none;">
    <div class="modal-content">
      <h3>New Notebook</h3>
      <input id="new-notebook-input" type="text" placeholder="Notebook name">
      <div class="modal-actions">
        <button id="btn-cancel-notebook" class="btn-secondary">Cancel</button>
        <button id="btn-confirm-notebook" class="btn-primary">Create</button>
      </div>
    </div>
  </div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./model":"src/model.ts","./types":"src/types.ts","./renderer":"src/renderer.ts","./editor":"src/editor.ts"},"src/model.ts":{"./types":"src/types.ts","./constants":"src/constants.ts"},"src/renderer.ts":{"./types":"src/types.ts","./model":"src/model.ts"},"src/editor.ts":{"./constants":"src/constants.ts","./model":"src/model.ts","./renderer":"src/renderer.ts"}};
function __require(id) {
  if (__cache[id]) return __cache[id].exports;
  var module = __cache[id] = { exports: {} };
  var factory = __mods[id];
  if (!factory) throw new Error("Module not found: " + id);
  factory(module.exports, function (spec) {
    var target = (__map[id] && __map[id][spec]) || spec;
    return __require(target);
  }, module);
  return module.exports;
}

// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.start = start;
// ── Entry point: wires model, renderer, and editor together ────────────
const model_1 = require("./model");
const types_1 = require("./types");
const renderer_1 = require("./renderer");
const editor_1 = require("./editor");
// ── Start ──────────────────────────────────────────────────────────────
function start() {
    // Initialize model
    const state = (0, model_1.initModel)();
    // Cache DOM references
    (0, renderer_1.cacheDom)();
    // Subscribe to model changes
    (0, model_1.subscribe)(() => {
        (0, renderer_1.render)((0, model_1.getState)());
    });
    // Initial render
    (0, renderer_1.render)(state);
    // Initialize editor
    (0, editor_1.initEditor)();
    // Setup drag and drop
    (0, renderer_1.setupDragDrop)();
    // Wire up event handlers
    wireEvents();
    // Restore active note content
    if (state.activeNoteId) {
        (0, editor_1.loadNoteContent)(state.activeNoteId);
    }
    // Restore dark mode
    if (state.darkMode) {
        document.body.classList.add('dark-mode');
    }
}
// ── Event wiring ───────────────────────────────────────────────────────
function wireEvents() {
    const dom = getDomRefs();
    // Sidebar toggle
    dom.btnCollapse.addEventListener('click', () => {
        (0, model_1.toggleSidebar)();
    });
    // Dark mode toggle
    dom.btnDarkMode.addEventListener('click', () => {
        (0, model_1.toggleDarkMode)();
    });
    // New note
    dom.btnNewNote.addEventListener('click', () => {
        const state = (0, model_1.getState)();
        let notebookId = state.activeNotebookId;
        if (!notebookId || notebookId === types_1.NOTEBOOK_TRASH) {
            // Default to first notebook
            const firstNb = state.notebooks[0];
            if (firstNb)
                notebookId = firstNb.id;
        }
        if (notebookId) {
            const note = (0, model_1.createNote)(notebookId);
            (0, model_1.setActiveNote)(note.id);
            setTimeout(() => {
                (0, editor_1.loadNoteContent)(note.id);
                (0, renderer_1.focusEditor)();
            }, 50);
        }
    });
    // New notebook
    dom.btnNewNotebook.addEventListener('click', () => {
        (0, renderer_1.showNewNotebookDialog)();
    });
    dom.btnConfirmNotebook.addEventListener('click', () => {
        const name = dom.newNotebookInput.value.trim();
        if (name) {
            (0, model_1.createNotebook)(name);
            (0, renderer_1.hideNewNotebookDialog)();
        }
    });
    dom.btnCancelNotebook.addEventListener('click', () => {
        (0, renderer_1.hideNewNotebookDialog)();
    });
    dom.newNotebookInput.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') {
            const name = dom.newNotebookInput.value.trim();
            if (name) {
                (0, model_1.createNotebook)(name);
                (0, renderer_1.hideNewNotebookDialog)();
            }
        }
        if (e.key === 'Escape') {
            (0, renderer_1.hideNewNotebookDialog)();
        }
    });
    // Sort toggle
    dom.btnSort.addEventListener('click', (e) => {
        e.stopPropagation();
        (0, renderer_1.showSortDropdown)();
    });
    // Sort dropdown selection
    dom.sortDropdown.addEventListener('click', (e) => {
        const item = e.target.closest('.dropdown-item');
        if (item) {
            const sort = item.dataset.sort;
            if (sort) {
                (0, model_1.setSortOrder)(sort);
                (0, renderer_1.hideAllDropdowns)();
            }
        }
    });
    // Pin note
    dom.btnPin.addEventListener('click', () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId)
            (0, model_1.togglePin)(state.activeNoteId);
    });
    // Duplicate note
    dom.btnDuplicate.addEventListener('click', () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            const dup = (0, model_1.duplicateNote)(state.activeNoteId);
            if (dup) {
                (0, model_1.setActiveNote)(dup.id);
                setTimeout(() => (0, editor_1.loadNoteContent)(dup.id), 50);
            }
        }
    });
    // Delete note
    dom.btnDelete.addEventListener('click', () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            const note = (0, model_1.getNote)(state.activeNoteId);
            if (note && note.trashed) {
                // Already in trash: permanent delete
                (0, model_1.permanentlyDeleteNote)(state.activeNoteId);
            }
            else {
                (0, model_1.trashNote)(state.activeNoteId);
            }
        }
    });
    // Move note
    dom.btnMove.addEventListener('click', (e) => {
        e.stopPropagation();
        (0, renderer_1.showMoveDropdown)();
    });
    dom.moveDropdown.addEventListener('click', (e) => {
        const item = e.target.closest('.dropdown-item');
        if (item) {
            const nbId = item.dataset.moveTo;
            if (nbId) {
                const state = (0, model_1.getState)();
                if (state.activeNoteId)
                    (0, model_1.moveNote)(state.activeNoteId, nbId);
                (0, renderer_1.hideAllDropdowns)();
            }
        }
    });
    // Search input
    dom.searchInput.addEventListener('input', (e) => {
        (0, model_1.setSearchQuery)(e.target.value);
    });
    // Toolbar buttons
    dom.editorToolbar.addEventListener('click', (e) => {
        const btn = e.target.closest('.toolbar-btn');
        if (btn && btn.dataset.cmd) {
            (0, editor_1.handleToolbarCommand)(btn.dataset.cmd);
        }
    });
    // Close dropdowns on outside click
    document.addEventListener('click', (e) => {
        const target = e.target;
        // Close move dropdown
        if (!target.closest('#move-dropdown') && !target.closest('#btn-move-note')) {
            dom.moveDropdown.style.display = 'none';
        }
        // Close sort dropdown
        if (!target.closest('#sort-dropdown') && !target.closest('#btn-sort')) {
            dom.sortDropdown.style.display = 'none';
        }
        // Close trash menu
        if (!target.closest('#trash-menu')) {
            dom.trashMenu.style.display = 'none';
        }
        // Close notebook dialog on overlay click
        if (target.closest('#new-notebook-dialog') && !target.closest('.modal-content')) {
            (0, renderer_1.hideNewNotebookDialog)();
        }
    });
    // Note list: right-click for trash actions
    dom.noteListContainer.addEventListener('contextmenu', (e) => {
        const state = (0, model_1.getState)();
        if (state.activeNotebookId === types_1.NOTEBOOK_TRASH) {
            e.preventDefault();
            const noteItem = e.target.closest('.note-item');
            if (noteItem) {
                (0, renderer_1.showTrashMenu)(e.clientX, e.clientY);
            }
        }
    });
    dom.trashMenu.addEventListener('click', (e) => {
        const item = e.target.closest('.dropdown-item');
        if (item) {
            const action = item.dataset.action;
            const state = (0, model_1.getState)();
            if (state.activeNoteId) {
                if (action === 'restore')
                    (0, model_1.restoreNote)(state.activeNoteId);
                if (action === 'permanent-delete')
                    (0, model_1.permanentlyDeleteNote)(state.activeNoteId);
            }
            (0, renderer_1.hideAllDropdowns)();
        }
    });
    // Pane resizing
    setupResizeHandle(dom.handle1, (delta) => {
        const state = (0, model_1.getState)();
        const newWidth = Math.max(160, Math.min(320, state.layout.sidebarWidth + delta));
        state.layout.sidebarWidth = newWidth;
        // Force re-render for width change
        (0, renderer_1.render)(state);
    });
    setupResizeHandle(dom.handle2, (delta) => {
        const state = (0, model_1.getState)();
        const newWidth = Math.max(200, Math.min(450, state.layout.noteListWidth + delta));
        state.layout.noteListWidth = newWidth;
        (0, renderer_1.render)(state);
    });
    // Note selection handler (called from renderer dispatchers)
    window.__onNoteSelect = (noteId) => {
        (0, model_1.setActiveNote)(noteId);
        setTimeout(() => {
            const note = (0, model_1.getNote)(noteId);
            if (note) {
                (0, editor_1.loadNoteContent)(noteId);
            }
        }, 10);
    };
    // Notebook selection handler
    window.__onNotebookSelect = (nbId) => {
        (0, model_1.setActiveNotebook)(nbId);
        (0, model_1.setActiveNote)(null);
        (0, editor_1.clearEditor)();
    };
    // Collapse handler
    window.__onCollapse = (nbId) => {
        (0, model_1.toggleNotebookCollapse)(nbId);
    };
    // Rename notebook handler
    window.__onRenameNotebook = (nbId) => {
        const nb = (0, model_1.getState)().notebooks.find(n => n.id === nbId);
        if (!nb)
            return;
        const newName = prompt('Rename notebook:', nb.name);
        if (newName && newName.trim()) {
            (0, model_1.renameNotebook)(nbId, newName.trim());
        }
    };
    // Delete notebook handler
    window.__onDeleteNotebook = (nbId) => {
        const nb = (0, model_1.getState)().notebooks.find(n => n.id === nbId);
        if (!nb)
            return;
        if (confirm(`Delete "${nb.name}"? Notes will be moved to Trash.`)) {
            (0, model_1.deleteNotebook)(nbId);
        }
    };
    // Tag click handler
    window.__onTagClick = (tag) => {
        (0, model_1.setTagFilter)(tag);
    };
    // Tag remove handler
    window.__onTagRemove = (tag) => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            const note = (0, model_1.getNote)(state.activeNoteId);
            if (note) {
                (0, model_1.updateNoteTags)(state.activeNoteId, note.tags.filter(t => t !== tag));
            }
        }
    };
    // Tag add handler
    window.__onTagAdd = () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            const input = document.getElementById('tag-input');
            const tag = input.value.trim().toLowerCase().replace(/[^a-z0-9-]/g, '');
            if (tag) {
                const note = (0, model_1.getNote)(state.activeNoteId);
                if (note && !note.tags.includes(tag)) {
                    (0, model_1.updateNoteTags)(state.activeNoteId, [...note.tags, tag]);
                }
                input.value = '';
            }
        }
    };
    // Sort select handler
    window.__onSortSelect = (sort) => {
        (0, model_1.setSortOrder)(sort);
    };
    // Move to notebook handler
    window.__onMoveToNotebook = (nbId) => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            (0, model_1.moveNote)(state.activeNoteId, nbId);
        }
    };
    // Notebook created handler
    window.__onNotebookCreated = (name) => {
        (0, model_1.createNotebook)(name);
    };
    // Notebook cancelled handler
    window.__onNotebookCancelled = () => {
        (0, renderer_1.hideNewNotebookDialog)();
    };
    // Search input handler
    window.__onSearchInput = (value) => {
        (0, model_1.setSearchQuery)(value);
    };
    // Pin handler
    window.__onPinNote = () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId)
            (0, model_1.togglePin)(state.activeNoteId);
    };
    // Duplicate handler
    window.__onDuplicateNote = () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            const dup = (0, model_1.duplicateNote)(state.activeNoteId);
            if (dup) {
                (0, model_1.setActiveNote)(dup.id);
                setTimeout(() => (0, editor_1.loadNoteContent)(dup.id), 50);
            }
        }
    };
    // Delete handler
    window.__onDeleteNote = () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            const note = (0, model_1.getNote)(state.activeNoteId);
            if (note && note.trashed) {
                (0, model_1.permanentlyDeleteNote)(state.activeNoteId);
            }
            else {
                (0, model_1.trashNote)(state.activeNoteId);
            }
        }
    };
    // Move handler
    window.__onMoveNote = () => {
        (0, renderer_1.showMoveDropdown)();
    };
    // Restore handler
    window.__onRestoreNote = () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId)
            (0, model_1.restoreNote)(state.activeNoteId);
    };
    // Permanent delete handler
    window.__onPermanentDeleteNote = () => {
        const state = (0, model_1.getState)();
        if (state.activeNoteId)
            (0, model_1.permanentlyDeleteNote)(state.activeNoteId);
    };
}
// ── DOM reference shortcut ─────────────────────────────────────────────
function getDomRefs() {
    return {
        btnCollapse: document.getElementById('btn-collapse-sidebar'),
        btnDarkMode: document.getElementById('btn-dark-mode'),
        btnNewNote: document.getElementById('btn-new-note'),
        btnNewNotebook: document.getElementById('btn-new-notebook'),
        btnSort: document.getElementById('btn-sort'),
        btnPin: document.getElementById('btn-pin-note'),
        btnDuplicate: document.getElementById('btn-duplicate-note'),
        btnDelete: document.getElementById('btn-delete-note'),
        btnMove: document.getElementById('btn-move-note'),
        searchInput: document.getElementById('search-input'),
        editorToolbar: document.getElementById('editor-toolbar'),
        noteListContainer: document.getElementById('note-list-container'),
        moveDropdown: document.getElementById('move-dropdown'),
        sortDropdown: document.getElementById('sort-dropdown'),
        trashMenu: document.getElementById('trash-menu'),
        newNotebookDialog: document.getElementById('new-notebook-dialog'),
        newNotebookInput: document.getElementById('new-notebook-input'),
        btnConfirmNotebook: document.getElementById('btn-confirm-notebook'),
        btnCancelNotebook: document.getElementById('btn-cancel-notebook'),
        handle1: document.getElementById('handle1'),
        handle2: document.getElementById('handle2'),
    };
}
// ── Resize handle logic ────────────────────────────────────────────────
function setupResizeHandle(handle, onResize) {
    let startX = 0;
    let isResizing = false;
    handle.addEventListener('mousedown', (e) => {
        isResizing = true;
        startX = e.clientX;
        handle.classList.add('active');
        document.body.style.cursor = 'col-resize';
        document.body.style.userSelect = 'none';
        e.preventDefault();
    });
    document.addEventListener('mousemove', (e) => {
        if (!isResizing)
            return;
        const delta = e.clientX - startX;
        startX = e.clientX;
        onResize(delta);
    });
    document.addEventListener('mouseup', () => {
        if (!isResizing)
            return;
        isResizing = false;
        handle.classList.remove('active');
        document.body.style.cursor = '';
        document.body.style.userSelect = '';
    });
}
// ── Auto-start ─────────────────────────────────────────────────────────
start();
};

// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
// ── Core data model types ──────────────────────────────────────────────
Object.defineProperty(exports, "__esModule", { value: true });
exports.NOTEBOOK_TRASH = exports.NOTEBOOK_ALL = void 0;
exports.isVirtualNotebook = isVirtualNotebook;
// Virtual notebook IDs
exports.NOTEBOOK_ALL = 'all';
exports.NOTEBOOK_TRASH = 'trash';
function isVirtualNotebook(id) {
    return id === exports.NOTEBOOK_ALL || id === exports.NOTEBOOK_TRASH;
}
};

// ── module: src/constants.ts ──
__mods["src/constants.ts"] = function (exports, require, module) {
"use strict";
// ── Tunable constants ──────────────────────────────────────────────────
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_SORT_ORDER = exports.DEFAULT_LAYOUT = exports.FONTS = exports.COLORS = exports.NOTE_LIST_MAX_WIDTH = exports.NOTE_LIST_MIN_WIDTH = exports.SIDEBAR_MAX_WIDTH = exports.SIDEBAR_MIN_WIDTH = exports.MAX_PANE_WIDTH = exports.MIN_PANE_WIDTH = exports.SNIPPET_LENGTH = exports.AUTOSAVE_DEBOUNCE = exports.STORAGE_KEY = void 0;
exports.STORAGE_KEY = 'notes-app-state';
exports.AUTOSAVE_DEBOUNCE = 500;
exports.SNIPPET_LENGTH = 80;
exports.MIN_PANE_WIDTH = 180;
exports.MAX_PANE_WIDTH = 600;
exports.SIDEBAR_MIN_WIDTH = 160;
exports.SIDEBAR_MAX_WIDTH = 320;
exports.NOTE_LIST_MIN_WIDTH = 200;
exports.NOTE_LIST_MAX_WIDTH = 450;
// ── Color palette ──────────────────────────────────────────────────────
exports.COLORS = {
    // Light theme
    light: {
        bg: '#FAFAF9',
        bgSecondary: '#F3F2F0',
        bgTertiary: '#EBEAE7',
        bgHover: '#E8E7E4',
        bgActive: '#DFDED9',
        bgEditor: '#FFFFFF',
        text: '#2C2C2C',
        textSecondary: '#6B6B6B',
        textTertiary: '#9A9A9A',
        border: '#E5E4E1',
        borderLight: '#EDECE9',
        accent: '#6C5CE7',
        accentLight: '#A29BFE',
        accentBg: 'rgba(108, 92, 231, 0.08)',
        danger: '#E74C3C',
        dangerBg: 'rgba(231, 76, 60, 0.08)',
        success: '#27AE60',
        successBg: 'rgba(39, 174, 96, 0.08)',
        shadow: 'rgba(0, 0, 0, 0.06)',
        shadowMedium: 'rgba(0, 0, 0, 0.1)',
        tagBg: '#E8E7E4',
        tagText: '#5A5A5A',
        checkboxUnchecked: '#C4C4C4',
        checkboxChecked: '#6C5CE7',
        toolbarBg: '#FFFFFF',
        toolbarBorder: '#E5E4E1',
        toolbarHover: '#F3F2F0',
        toolbarActive: '#DFDED9',
        searchBg: '#F3F2F0',
        highlightBg: 'rgba(255, 235, 59, 0.3)',
    },
    // Dark theme
    dark: {
        bg: '#1A1A1A',
        bgSecondary: '#232323',
        bgTertiary: '#2A2A2A',
        bgHover: '#333333',
        bgActive: '#3A3A3A',
        bgEditor: '#1E1E1E',
        text: '#E8E8E8',
        textSecondary: '#A0A0A0',
        textTertiary: '#6B6B6B',
        border: '#333333',
        borderLight: '#2A2A2A',
        accent: '#A29BFE',
        accentLight: '#6C5CE7',
        accentBg: 'rgba(162, 155, 254, 0.12)',
        danger: '#FF6B6B',
        dangerBg: 'rgba(255, 107, 107, 0.12)',
        success: '#69DB7C',
        successBg: 'rgba(105, 219, 124, 0.12)',
        shadow: 'rgba(0, 0, 0, 0.3)',
        shadowMedium: 'rgba(0, 0, 0, 0.5)',
        tagBg: '#333333',
        tagText: '#B0B0B0',
        checkboxUnchecked: '#555555',
        checkboxChecked: '#A29BFE',
        toolbarBg: '#232323',
        toolbarBorder: '#333333',
        toolbarHover: '#2A2A2A',
        toolbarActive: '#3A3A3A',
        searchBg: '#2A2A2A',
        highlightBg: 'rgba(255, 235, 59, 0.2)',
    },
    // Notebook accent colors
    notebookColors: [
        '#6C5CE7', '#00B894', '#FDCB6E', '#E17055',
        '#0984E3', '#D63031', '#00CEC9', '#E84393',
        '#55A3F5', '#FF7675', '#74B9FF', '#A8E6CF',
    ],
};
// ── Font settings ──────────────────────────────────────────────────────
exports.FONTS = {
    ui: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
    editor: "'Georgia', 'Noto Serif', 'Source Serif Pro', 'Liberation Serif', 'Times New Roman', serif",
    editorSans: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
    mono: "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Cascadia Code', 'Consolas', monospace",
};
// ── Default state ──────────────────────────────────────────────────────
exports.DEFAULT_LAYOUT = {
    sidebarWidth: 240,
    noteListWidth: 300,
    sidebarCollapsed: false,
};
exports.DEFAULT_SORT_ORDER = 'modified';
};

// ── module: src/model.ts ──
__mods["src/model.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.genId = genId;
exports.initModel = initModel;
exports.saveState = saveState;
exports.getState = getState;
exports.getNote = getNote;
exports.getNotebook = getNotebook;
exports.getNotesForNotebook = getNotesForNotebook;
exports.createNotebook = createNotebook;
exports.renameNotebook = renameNotebook;
exports.deleteNotebook = deleteNotebook;
exports.toggleNotebookCollapse = toggleNotebookCollapse;
exports.createNote = createNote;
exports.updateNote = updateNote;
exports.updateNoteTags = updateNoteTags;
exports.togglePin = togglePin;
exports.duplicateNote = duplicateNote;
exports.trashNote = trashNote;
exports.restoreNote = restoreNote;
exports.permanentlyDeleteNote = permanentlyDeleteNote;
exports.moveNote = moveNote;
exports.setActiveNotebook = setActiveNotebook;
exports.setActiveNote = setActiveNote;
exports.setSortOrder = setSortOrder;
exports.setSearchQuery = setSearchQuery;
exports.setTagFilter = setTagFilter;
exports.getFilteredAndSortedNotes = getFilteredAndSortedNotes;
exports.setSidebarWidth = setSidebarWidth;
exports.setNoteListWidth = setNoteListWidth;
exports.toggleSidebar = toggleSidebar;
exports.toggleDarkMode = toggleDarkMode;
exports.extractPlainText = extractPlainText;
exports.getSnippet = getSnippet;
exports.sanitizeHtml = sanitizeHtml;
exports.subscribe = subscribe;
exports.formatTimestamp = formatTimestamp;
const types_1 = require("./types");
const constants_1 = require("./constants");
// ── ID generation ──────────────────────────────────────────────────────
let _idCounter = Date.now();
function genId() {
    return `id_${_idCounter++}_${Math.random().toString(36).slice(2, 8)}`;
}
// ── Model ──────────────────────────────────────────────────────────────
let state;
let listeners = new Set();
function initModel() {
    const loaded = loadState();
    if (loaded) {
        state = loaded;
    }
    else {
        state = createDefaultState();
    }
    return state;
}
function createDefaultState() {
    const notebook1 = genId();
    const notebook2 = genId();
    const now = Date.now();
    return {
        notebooks: [
            { id: notebook1, name: 'Personal', collapsed: false, color: constants_1.COLORS.notebookColors[0] },
            { id: notebook2, name: 'Work', collapsed: false, color: constants_1.COLORS.notebookColors[1] },
        ],
        notes: [
            {
                id: genId(), notebookId: notebook1, title: 'Welcome to Notes',
                content: '<h1>Welcome to Notes</h1><p>This is a <strong>rich text editor</strong> with markdown-style shortcuts.</p><ul><li>Type <code># </code> for headings</li><li>Type <code>- </code> for lists</li><li>Type <code>[] </code> for checklists</li></ul><p>Try it out!</p>',
                tags: ['welcome', 'getting-started'], pinned: true,
                createdAt: now, updatedAt: now, trashed: false,
            },
            {
                id: genId(), notebookId: notebook2, title: 'Meeting Notes',
                content: '<p>Discuss project timeline and deliverables.</p>',
                tags: ['work', 'meetings'], pinned: false,
                createdAt: now - 1000, updatedAt: now - 1000, trashed: false,
            },
        ],
        tags: [],
        activeNotebookId: types_1.NOTEBOOK_ALL,
        activeNoteId: null,
        sortOrder: constants_1.DEFAULT_SORT_ORDER,
        searchQuery: '',
        tagFilter: null,
        layout: constants_1.DEFAULT_LAYOUT,
        darkMode: false,
    };
}
// ── Persistence ────────────────────────────────────────────────────────
function saveState() {
    try {
        localStorage.setItem(constants_1.STORAGE_KEY, JSON.stringify(state));
    }
    catch (e) {
        console.warn('Failed to save state:', e);
    }
}
function loadState() {
    try {
        const raw = localStorage.getItem(constants_1.STORAGE_KEY);
        if (!raw)
            return null;
        const parsed = JSON.parse(raw);
        // Basic validation
        if (!parsed.notebooks || !parsed.notes)
            return null;
        return parsed;
    }
    catch {
        return null;
    }
}
// ── Getters ────────────────────────────────────────────────────────────
function getState() {
    return state;
}
function getNote(id) {
    return state.notes.find(n => n.id === id);
}
function getNotebook(id) {
    return state.notebooks.find(n => n.id === id);
}
function getNotesForNotebook(notebookId) {
    if (notebookId === types_1.NOTEBOOK_ALL)
        return state.notes.filter(n => !n.trashed);
    if (notebookId === types_1.NOTEBOOK_TRASH)
        return state.notes.filter(n => n.trashed);
    return state.notes.filter(n => n.notebookId === notebookId && !n.trashed);
}
// ── Notebook operations ────────────────────────────────────────────────
function createNotebook(name) {
    const color = constants_1.COLORS.notebookColors[state.notebooks.length % constants_1.COLORS.notebookColors.length];
    const nb = { id: genId(), name, collapsed: false, color };
    state.notebooks.push(nb);
    notify();
    return nb;
}
function renameNotebook(id, name) {
    const nb = getNotebook(id);
    if (nb) {
        nb.name = name;
        notify();
    }
}
function deleteNotebook(id) {
    const nb = getNotebook(id);
    if (!nb)
        return;
    // Move all notes to trash
    state.notes
        .filter(n => n.notebookId === id && !n.trashed)
        .forEach(n => { n.trashed = true; n.trashedAt = Date.now(); });
    state.notebooks = state.notebooks.filter(n => n.id !== id);
    if (state.activeNotebookId === id)
        state.activeNotebookId = types_1.NOTEBOOK_ALL;
    notify();
}
function toggleNotebookCollapse(id) {
    const nb = getNotebook(id);
    if (nb) {
        nb.collapsed = !nb.collapsed;
        notify();
    }
}
// ── Note operations ────────────────────────────────────────────────────
function createNote(notebookId, title = 'Untitled') {
    const now = Date.now();
    const note = {
        id: genId(), notebookId, title,
        content: '', tags: [], pinned: false,
        createdAt: now, updatedAt: now, trashed: false,
    };
    state.notes.push(note);
    notify();
    return note;
}
function updateNote(id, updates) {
    const note = getNote(id);
    if (note) {
        if (updates.title !== undefined)
            note.title = updates.title;
        if (updates.content !== undefined)
            note.content = updates.content;
        note.updatedAt = Date.now();
        notify();
    }
}
function updateNoteTags(id, tags) {
    const note = getNote(id);
    if (note) {
        note.tags = tags;
        note.updatedAt = Date.now();
        notify();
    }
}
function togglePin(id) {
    const note = getNote(id);
    if (note) {
        note.pinned = !note.pinned;
        note.updatedAt = Date.now();
        notify();
    }
}
function duplicateNote(id) {
    const note = getNote(id);
    if (!note)
        return undefined;
    const now = Date.now();
    const dup = {
        ...note,
        id: genId(),
        title: note.title + ' (copy)',
        createdAt: now,
        updatedAt: now,
        pinned: false,
        trashed: false,
        trashedAt: undefined,
    };
    state.notes.push(dup);
    notify();
    return dup;
}
function trashNote(id) {
    const note = getNote(id);
    if (note && !note.trashed) {
        note.trashed = true;
        note.trashedAt = Date.now();
        note.updatedAt = Date.now();
        if (state.activeNoteId === id)
            state.activeNoteId = null;
        notify();
    }
}
function restoreNote(id) {
    const note = getNote(id);
    if (note && note.trashed) {
        note.trashed = false;
        note.trashedAt = undefined;
        note.updatedAt = Date.now();
        notify();
    }
}
function permanentlyDeleteNote(id) {
    state.notes = state.notes.filter(n => n.id !== id);
    if (state.activeNoteId === id)
        state.activeNoteId = null;
    notify();
}
function moveNote(id, targetNotebookId) {
    if ((0, types_1.isVirtualNotebook)(targetNotebookId))
        return;
    const note = getNote(id);
    if (note) {
        note.notebookId = targetNotebookId;
        note.updatedAt = Date.now();
        notify();
    }
}
// ── Selection ──────────────────────────────────────────────────────────
function setActiveNotebook(id) {
    state.activeNotebookId = id;
    state.tagFilter = null;
    notify();
}
function setActiveNote(id) {
    state.activeNoteId = id;
    notify();
}
// ── Sort & filter ──────────────────────────────────────────────────────
function setSortOrder(order) {
    state.sortOrder = order;
    notify();
}
function setSearchQuery(query) {
    state.searchQuery = query;
    notify();
}
function setTagFilter(tag) {
    state.tagFilter = tag;
    notify();
}
function getFilteredAndSortedNotes() {
    const nbId = state.activeNotebookId;
    let notes = nbId ? getNotesForNotebook(nbId) : [];
    // Tag filter
    if (state.tagFilter) {
        const tag = state.tagFilter;
        notes = notes.filter(n => n.tags.includes(tag));
    }
    // Search
    if (state.searchQuery) {
        const q = state.searchQuery.toLowerCase();
        notes = notes.filter(n => {
            const titleMatch = n.title.toLowerCase().includes(q);
            const contentMatch = extractPlainText(n.content).toLowerCase().includes(q);
            return titleMatch || contentMatch;
        });
    }
    // Sort: pinned first, then by sort order
    notes.sort((a, b) => {
        if (a.pinned !== b.pinned)
            return a.pinned ? -1 : 1;
        switch (state.sortOrder) {
            case 'modified': return b.updatedAt - a.updatedAt;
            case 'created': return b.createdAt - a.createdAt;
            case 'alphabetical': return a.title.localeCompare(b.title);
            default: return 0;
        }
    });
    return notes;
}
// ── Layout ─────────────────────────────────────────────────────────────
function setSidebarWidth(width) {
    state.layout.sidebarWidth = width;
    notify();
}
function setNoteListWidth(width) {
    state.layout.noteListWidth = width;
    notify();
}
function toggleSidebar() {
    state.layout.sidebarCollapsed = !state.layout.sidebarCollapsed;
    notify();
}
function toggleDarkMode() {
    state.darkMode = !state.darkMode;
    notify();
}
// ── Text extraction (pure, no HTML tags) ───────────────────────────────
function extractPlainText(html) {
    if (!html)
        return '';
    const div = document.createElement('div');
    div.innerHTML = html;
    return (div.textContent || div.innerText || '').trim();
}
// ── Snippet generation ─────────────────────────────────────────────────
function getSnippet(html, maxLength = 80) {
    const text = extractPlainText(html);
    if (text.length <= maxLength)
        return text;
    return text.slice(0, maxLength).trimEnd() + '…';
}
// ── Sanitization ───────────────────────────────────────────────────────
const ALLOWED_TAGS = new Set([
    'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'strike', 'del', 'code',
    'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'blockquote', 'hr',
    'div', 'span', 'a',
]);
function sanitizeHtml(html) {
    const div = document.createElement('div');
    div.innerHTML = html;
    // Remove disallowed tags (keep their children)
    const walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT);
    const toRemove = [];
    while (walker.nextNode()) {
        const el = walker.currentNode;
        if (!ALLOWED_TAGS.has(el.tagName.toLowerCase())) {
            toRemove.push(el);
        }
    }
    for (const el of toRemove) {
        // Replace with text content
        const text = document.createTextNode(el.textContent || '');
        el.parentNode?.replaceChild(text, el);
    }
    // Remove all attributes except those needed for checklist state
    const allEls = div.querySelectorAll('*');
    for (const el of allEls) {
        const attrs = Array.from(el.attributes);
        for (const attr of attrs) {
            // Preserve class on checklist items and checkboxes
            if (attr.name === 'class' && (el.tagName.toLowerCase() === 'li' ||
                el.tagName.toLowerCase() === 'span'))
                continue;
            // Preserve data-* on li elements
            if (el.tagName.toLowerCase() === 'li' && attr.name.startsWith('data-'))
                continue;
            el.removeAttribute(attr.name);
        }
    }
    return div.innerHTML;
}
// ── Reactive notifications ─────────────────────────────────────────────
function subscribe(listener) {
    listeners.add(listener);
    return () => listeners.delete(listener);
}
function notify() {
    saveState();
    for (const fn of listeners)
        fn();
}
// ── Timestamp formatting ───────────────────────────────────────────────
function formatTimestamp(ts) {
    const now = Date.now();
    const diff = now - ts;
    const minute = 60 * 1000;
    const hour = 60 * minute;
    const day = 24 * hour;
    const week = 7 * day;
    if (diff < minute)
        return 'Just now';
    if (diff < hour)
        return `${Math.floor(diff / minute)}m ago`;
    if (diff < day)
        return `${Math.floor(diff / hour)}h ago`;
    if (diff < week)
        return `${Math.floor(diff / day)}d ago`;
    const date = new Date(ts);
    return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: date.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined });
}
};

// ── module: src/renderer.ts ──
__mods["src/renderer.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cacheDom = cacheDom;
exports.render = render;
exports.renderTheme = renderTheme;
exports.renderLayout = renderLayout;
exports.renderSidebar = renderSidebar;
exports.renderNoteList = renderNoteList;
exports.renderEditorVisibility = renderEditorVisibility;
exports.renderEditorState = renderEditorState;
exports.showSavedIndicator = showSavedIndicator;
exports.dispatchNotebookSelect = dispatchNotebookSelect;
exports.dispatchNoteSelect = dispatchNoteSelect;
exports.dispatchCollapse = dispatchCollapse;
exports.dispatchRenameNotebook = dispatchRenameNotebook;
exports.dispatchDeleteNotebook = dispatchDeleteNotebook;
exports.dispatchNewNote = dispatchNewNote;
exports.dispatchNewNotebook = dispatchNewNotebook;
exports.dispatchToggleSidebar = dispatchToggleSidebar;
exports.dispatchToggleDarkMode = dispatchToggleDarkMode;
exports.dispatchSortToggle = dispatchSortToggle;
exports.dispatchPinNote = dispatchPinNote;
exports.dispatchDuplicateNote = dispatchDuplicateNote;
exports.dispatchDeleteNote = dispatchDeleteNote;
exports.dispatchMoveNote = dispatchMoveNote;
exports.dispatchRestoreNote = dispatchRestoreNote;
exports.dispatchPermanentDeleteNote = dispatchPermanentDeleteNote;
exports.dispatchSearchInput = dispatchSearchInput;
exports.dispatchTagClick = dispatchTagClick;
exports.dispatchTagRemove = dispatchTagRemove;
exports.dispatchTagAdd = dispatchTagAdd;
exports.dispatchSortSelect = dispatchSortSelect;
exports.dispatchMoveToNotebook = dispatchMoveToNotebook;
exports.dispatchNotebookCreated = dispatchNotebookCreated;
exports.dispatchNotebookCancelled = dispatchNotebookCancelled;
exports.getEditorContent = getEditorContent;
exports.setEditorContent = setEditorContent;
exports.focusEditor = focusEditor;
exports.showMoveDropdown = showMoveDropdown;
exports.showSortDropdown = showSortDropdown;
exports.showTrashMenu = showTrashMenu;
exports.hideAllDropdowns = hideAllDropdowns;
exports.showNewNotebookDialog = showNewNotebookDialog;
exports.hideNewNotebookDialog = hideNewNotebookDialog;
exports.updateToolbarState = updateToolbarState;
exports.setupDragDrop = setupDragDrop;
const types_1 = require("./types");
const model_1 = require("./model");
// ── DOM element references ─────────────────────────────────────────────
let dom = {};
let searchInput;
let editorContent;
let editorTitle;
let savedIndicator;
let editorBody;
let editorEmptyState;
let tagInput;
function cacheDom() {
    dom = {
        sidebar: document.getElementById('sidebar'),
        sidebarList: document.getElementById('sidebar-list'),
        sidebarVirtual: document.getElementById('sidebar-virtual-items'),
        sidebarNotebooks: document.getElementById('sidebar-notebooks'),
        noteListPane: document.getElementById('note-list-pane'),
        noteListTitle: document.getElementById('note-list-title'),
        noteListCount: document.getElementById('note-list-count'),
        noteListContainer: document.getElementById('note-list-container'),
        editorPane: document.getElementById('editor-pane'),
        editorBody: document.getElementById('editor-body'),
        editorContent: document.getElementById('editor-content'),
        editorTitle: document.getElementById('editor-title'),
        savedIndicator: document.getElementById('saved-indicator'),
        editorEmptyState: document.getElementById('editor-empty-state'),
        tagsContainer: document.getElementById('tags-container'),
        tagInput: document.getElementById('tag-input'),
        editorHeader: document.getElementById('editor-header'),
        editorToolbar: document.getElementById('editor-toolbar'),
        editorTagsArea: document.getElementById('editor-tags-area'),
        btnCollapse: document.getElementById('btn-collapse-sidebar'),
        btnNewNote: document.getElementById('btn-new-note'),
        btnNewNotebook: document.getElementById('btn-new-notebook'),
        btnSort: document.getElementById('btn-sort'),
        btnMove: document.getElementById('btn-move-note'),
        btnDuplicate: document.getElementById('btn-duplicate-note'),
        btnPin: document.getElementById('btn-pin-note'),
        btnDelete: document.getElementById('btn-delete-note'),
        btnDarkMode: document.getElementById('btn-dark-mode'),
        moveDropdown: document.getElementById('move-dropdown'),
        sortDropdown: document.getElementById('sort-dropdown'),
        trashMenu: document.getElementById('trash-menu'),
        newNotebookDialog: document.getElementById('new-notebook-dialog'),
        newNotebookInput: document.getElementById('new-notebook-input'),
        btnConfirmNotebook: document.getElementById('btn-confirm-notebook'),
        btnCancelNotebook: document.getElementById('btn-cancel-notebook'),
        handle1: document.getElementById('handle1'),
        handle2: document.getElementById('handle2'),
    };
    searchInput = document.getElementById('search-input');
    editorContent = dom.editorContent;
    editorTitle = dom.editorTitle;
    savedIndicator = dom.savedIndicator;
    editorBody = dom.editorBody;
    editorEmptyState = dom.editorEmptyState;
    tagInput = dom.tagInput;
}
// ── Full re-render ─────────────────────────────────────────────────────
function render(state) {
    renderTheme(state);
    renderLayout(state.layout);
    renderSidebar(state);
    renderNoteList(state);
    renderEditorState(state);
    renderEditorVisibility(state);
    updateSavedIndicator(false);
}
// ── Theme ──────────────────────────────────────────────────────────────
let lastDarkMode = null;
function renderTheme(state) {
    if (state.darkMode === lastDarkMode)
        return;
    lastDarkMode = state.darkMode;
    document.body.classList.toggle('dark-mode', state.darkMode);
}
// ── Layout ─────────────────────────────────────────────────────────────
let lastSidebarCollapsed = null;
function renderLayout(layout) {
    // Sidebar collapse
    if (layout.sidebarCollapsed !== lastSidebarCollapsed) {
        lastSidebarCollapsed = layout.sidebarCollapsed;
        dom.sidebar.classList.toggle('collapsed', layout.sidebarCollapsed);
        dom.btnCollapse.textContent = layout.sidebarCollapsed ? '▶' : '◀';
        dom.handle1.style.display = layout.sidebarCollapsed ? 'none' : '';
    }
    // Widths
    dom.sidebar.style.width = layout.sidebarWidth + 'px';
    dom.noteListPane.style.width = layout.noteListWidth + 'px';
}
// ── Sidebar ────────────────────────────────────────────────────────────
let lastSidebarKey = '';
function renderSidebar(state) {
    const key = JSON.stringify({
        nbCount: state.notebooks.length,
        nbNames: state.notebooks.map(n => n.name),
        nbCollapsed: state.notebooks.map(n => n.collapsed),
        noteCounts: state.notebooks.map(nb => state.notes.filter(n => n.notebookId === nb.id && !n.trashed).length),
        trashCount: state.notes.filter(n => n.trashed).length,
        active: state.activeNotebookId,
    });
    if (key === lastSidebarKey)
        return;
    lastSidebarKey = key;
    // Virtual items: All Notes + Trash
    const allCount = state.notes.filter(n => !n.trashed).length;
    const trashCount = state.notes.filter(n => n.trashed).length;
    dom.sidebarVirtual.innerHTML = `
    ${renderVirtualItem('📋', 'All Notes', types_1.NOTEBOOK_ALL, allCount, state.activeNotebookId === types_1.NOTEBOOK_ALL)}
    ${renderVirtualItem('🗑', 'Trash', types_1.NOTEBOOK_TRASH, trashCount, state.activeNotebookId === types_1.NOTEBOOK_TRASH)}
  `;
    // Notebooks
    let html = '';
    for (const nb of state.notebooks) {
        const notes = state.notes.filter(n => n.notebookId === nb.id && !n.trashed);
        const isActive = state.activeNotebookId === nb.id;
        html += renderNotebookItem(nb, notes.length, isActive, state);
    }
    dom.sidebarNotebooks.innerHTML = html;
    // Bind events
    bindSidebarEvents(state);
}
function renderVirtualItem(icon, name, id, count, active) {
    return `<div class="sidebar-item ${active ? 'active' : ''}" data-notebook="${id}">
    <span class="sidebar-item-icon">${icon}</span>
    <span class="sidebar-item-name">${name}</span>
    <span class="sidebar-item-count">${count}</span>
  </div>`;
}
function renderNotebookItem(nb, count, active, state) {
    const notes = state.notes.filter(n => n.notebookId === nb.id && !n.trashed);
    let notesHtml = '';
    if (!nb.collapsed) {
        for (const note of notes.slice(0, 10)) {
            notesHtml += `<div class="sidebar-note-item ${state.activeNoteId === note.id ? 'active' : ''}" data-note="${note.id}" data-notebook="${nb.id}">
        ${escapeHtml(note.title || 'Untitled')}
      </div>`;
        }
    }
    return `<div class="notebook-group">
    <div class="sidebar-item notebook-header ${active ? 'active' : ''}" data-notebook="${nb.id}">
      <span class="notebook-dot" style="background:${nb.color}"></span>
      <span class="sidebar-item-name">${escapeHtml(nb.name)}</span>
      <span class="sidebar-item-count">${count}</span>
      <div class="sidebar-item-actions">
        <button class="sidebar-item-action" data-action="collapse" data-notebook="${nb.id}" title="Collapse">▾</button>
        <button class="sidebar-item-action" data-action="rename" data-notebook="${nb.id}" title="Rename">✎</button>
        <button class="sidebar-item-action delete" data-action="delete-nb" data-notebook="${nb.id}" title="Delete">✕</button>
      </div>
    </div>
    <div class="notebook-notes ${nb.collapsed ? 'collapsed' : ''}" data-notebook-notes="${nb.id}">
      ${notesHtml}
    </div>
  </div>`;
}
function bindSidebarEvents(state) {
    // Notebook selection
    dom.sidebarVirtual.addEventListener('click', (e) => {
        const item = e.target.closest('[data-notebook]');
        if (!item)
            return;
        const id = item.dataset.notebook;
        if (id)
            dispatchNotebookSelect(id);
    });
    dom.sidebarNotebooks.addEventListener('click', (e) => {
        const btn = e.target;
        // Action buttons
        if (btn.dataset.action === 'collapse') {
            e.stopPropagation();
            const nbId = btn.dataset.notebook;
            if (nbId)
                dispatchCollapse(nbId);
            return;
        }
        if (btn.dataset.action === 'rename') {
            e.stopPropagation();
            const nbId = btn.dataset.notebook;
            if (nbId)
                dispatchRenameNotebook(nbId);
            return;
        }
        if (btn.dataset.action === 'delete-nb') {
            e.stopPropagation();
            const nbId = btn.dataset.notebook;
            if (nbId)
                dispatchDeleteNotebook(nbId);
            return;
        }
        // Notebook or note selection
        const item = e.target.closest('[data-notebook]') || e.target.closest('[data-note]');
        if (item) {
            const nbId = item.dataset.notebook;
            if (nbId)
                dispatchNotebookSelect(nbId);
            const noteId = item.dataset.note;
            if (noteId)
                dispatchNoteSelect(noteId);
        }
    });
}
// ── Note List ──────────────────────────────────────────────────────────
let lastNoteListKey = '';
function renderNoteList(state) {
    const notes = (0, model_1.getFilteredAndSortedNotes)();
    const nbId = state.activeNotebookId;
    // Title
    let titleText = 'All Notes';
    if (nbId === types_1.NOTEBOOK_TRASH)
        titleText = 'Trash';
    else {
        const nb = state.notebooks.find(n => n.id === nbId);
        if (nb)
            titleText = nb.name;
    }
    dom.noteListTitle.textContent = titleText;
    dom.noteListCount.textContent = `${notes.length} note${notes.length !== 1 ? 's' : ''}`;
    // Search input sync
    if (searchInput.value !== state.searchQuery) {
        searchInput.value = state.searchQuery;
    }
    // Tag filter indicator
    if (state.tagFilter) {
        dom.noteListTitle.textContent = `#${state.tagFilter}`;
    }
    // Render notes
    const key = JSON.stringify(notes.map(n => ({
        id: n.id, title: n.title, pinned: n.pinned, updatedAt: n.updatedAt,
        tags: n.tags, contentLen: n.content.length,
    })));
    if (key === lastNoteListKey && notes.length === dom.noteListContainer.children.length)
        return;
    lastNoteListKey = key;
    let html = '';
    for (const note of notes) {
        html += renderNoteItem(note, state);
    }
    dom.noteListContainer.innerHTML = html;
    bindNoteListEvents();
}
function bindNoteListEvents() {
    dom.noteListContainer.onclick = (e) => {
        // Tag click
        const tagEl = e.target.closest('.note-item-tag');
        if (tagEl) {
            const tag = tagEl.dataset.tag;
            if (tag)
                dispatchTagClick(tag);
            return;
        }
        // Note item click
        const noteItem = e.target.closest('.note-item');
        if (noteItem) {
            const noteId = noteItem.dataset.noteId;
            if (noteId)
                dispatchNoteSelect(noteId);
        }
    };
    // Right-click for trash actions
    dom.noteListContainer.oncontextmenu = (e) => {
        const state = (0, model_1.getState)();
        if (state.activeNotebookId === types_1.NOTEBOOK_TRASH) {
            e.preventDefault();
            const noteItem = e.target.closest('.note-item');
            if (noteItem) {
                // Select this note first
                const noteId = noteItem.dataset.noteId;
                if (noteId)
                    dispatchNoteSelect(noteId);
                showTrashMenu(e.clientX, e.clientY);
            }
        }
    };
}
function renderNoteItem(note, state) {
    const snippet = (0, model_1.getSnippet)(note.content, 80);
    const timestamp = (0, model_1.formatTimestamp)(note.updatedAt);
    const isActive = state.activeNoteId === note.id;
    const title = note.title || 'Untitled';
    const searchQuery = state.searchQuery;
    const highlightedTitle = searchQuery ? highlightText(title, searchQuery) : escapeHtml(title);
    const highlightedSnippet = searchQuery ? highlightText(snippet, searchQuery) : escapeHtml(snippet);
    let tagsHtml = '';
    if (note.tags.length > 0) {
        tagsHtml = `<div class="note-item-tags">
      ${note.tags.map(t => `<span class="note-item-tag" data-tag="${escapeHtml(t)}">${escapeHtml(t)}</span>`).join('')}
    </div>`;
    }
    const pinIcon = note.pinned ? '<span class="pin-indicator">📌</span>' : '';
    return `<div class="note-item ${isActive ? 'active' : ''}" data-note-id="${note.id}">
    <div class="note-item-title">${pinIcon}${highlightedTitle}</div>
    <div class="note-item-snippet">${highlightedSnippet}</div>
    <div class="note-item-meta">
      <span>${timestamp}</span>
      ${tagsHtml}
    </div>
  </div>`;
}
function highlightText(text, query) {
    if (!query)
        return escapeHtml(text);
    const escaped = escapeHtml(text);
    const escapedQuery = escapeHtml(query);
    const regex = new RegExp(`(${escapeRegex(escapedQuery)})`, 'gi');
    return escaped.replace(regex, '<mark class="search-highlight">$1</mark>');
}
function escapeRegex(s) {
    return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function escapeHtml(s) {
    const div = document.createElement('div');
    div.textContent = s;
    return div.innerHTML;
}
// ── Editor visibility ──────────────────────────────────────────────────
let lastActiveNoteId = null;
function renderEditorVisibility(state) {
    if (state.activeNoteId === lastActiveNoteId)
        return;
    lastActiveNoteId = state.activeNoteId;
    const hasNote = !!state.activeNoteId;
    editorBody.style.display = hasNote ? '' : 'none';
    dom.editorHeader.style.display = hasNote ? '' : 'none';
    dom.editorToolbar.style.display = hasNote ? '' : 'none';
    dom.editorTagsArea.style.display = hasNote ? '' : 'none';
    editorEmptyState.style.display = hasNote ? 'none' : 'flex';
}
// ── Editor state ───────────────────────────────────────────────────────
function renderEditorState(state) {
    const note = state.activeNoteId ? (0, model_1.getNote)(state.activeNoteId) : null;
    if (!note)
        return;
    // Title
    if (editorTitle.value !== note.title) {
        editorTitle.value = note.title;
    }
    // Tags
    renderTags(note);
}
function renderTags(note) {
    const container = dom.tagsContainer;
    const existingTags = Array.from(container.querySelectorAll('.tag-chip')).map(el => el.dataset.tag);
    const newTags = note.tags;
    // Simple diff: rebuild if different
    const key = JSON.stringify(newTags);
    if (container._tagKey === key)
        return;
    container._tagKey = key;
    container.innerHTML = note.tags.map(t => `<span class="tag-chip" data-tag="${escapeHtml(t)}">${escapeHtml(t)}<span class="tag-remove" data-tag="${escapeHtml(t)}">×</span></span>`).join('');
}
// ── Saved indicator ────────────────────────────────────────────────────
let savedTimeout = null;
function showSavedIndicator() {
    savedIndicator.classList.add('show');
    if (savedTimeout)
        clearTimeout(savedTimeout);
    savedTimeout = setTimeout(() => {
        savedIndicator.classList.remove('show');
    }, 1500);
}
function updateSavedIndicator(show) {
    savedIndicator.classList.toggle('show', show);
}
// ── Event dispatchers (bridge to model) ────────────────────────────────
function dispatchNotebookSelect(id) {
    const handler = window;
    if (handler.__onNotebookSelect)
        handler.__onNotebookSelect(id);
}
function dispatchNoteSelect(id) {
    const handler = window;
    if (handler.__onNoteSelect)
        handler.__onNoteSelect(id);
}
function dispatchCollapse(nbId) {
    const handler = window;
    if (handler.__onCollapse)
        handler.__onCollapse(nbId);
}
function dispatchRenameNotebook(nbId) {
    const handler = window;
    if (handler.__onRenameNotebook)
        handler.__onRenameNotebook(nbId);
}
function dispatchDeleteNotebook(nbId) {
    const handler = window;
    if (handler.__onDeleteNotebook)
        handler.__onDeleteNotebook(nbId);
}
function dispatchNewNote() {
    const handler = window;
    if (handler.__onNewNote)
        handler.__onNewNote();
}
function dispatchNewNotebook() {
    const handler = window;
    if (handler.__onNewNotebook)
        handler.__onNewNotebook();
}
function dispatchToggleSidebar() {
    const handler = window;
    if (handler.__onToggleSidebar)
        handler.__onToggleSidebar();
}
function dispatchToggleDarkMode() {
    const handler = window;
    if (handler.__onToggleDarkMode)
        handler.__onToggleDarkMode();
}
function dispatchSortToggle() {
    const handler = window;
    if (handler.__onSortToggle)
        handler.__onSortToggle();
}
function dispatchPinNote() {
    const handler = window;
    if (handler.__onPinNote)
        handler.__onPinNote();
}
function dispatchDuplicateNote() {
    const handler = window;
    if (handler.__onDuplicateNote)
        handler.__onDuplicateNote();
}
function dispatchDeleteNote() {
    const handler = window;
    if (handler.__onDeleteNote)
        handler.__onDeleteNote();
}
function dispatchMoveNote() {
    const handler = window;
    if (handler.__onMoveNote)
        handler.__onMoveNote();
}
function dispatchRestoreNote() {
    const handler = window;
    if (handler.__onRestoreNote)
        handler.__onRestoreNote();
}
function dispatchPermanentDeleteNote() {
    const handler = window;
    if (handler.__onPermanentDeleteNote)
        handler.__onPermanentDeleteNote();
}
function dispatchSearchInput(value) {
    const handler = window;
    if (handler.__onSearchInput)
        handler.__onSearchInput(value);
}
function dispatchTagClick(tag) {
    const handler = window;
    if (handler.__onTagClick)
        handler.__onTagClick(tag);
}
function dispatchTagRemove(tag) {
    const handler = window;
    if (handler.__onTagRemove)
        handler.__onTagRemove(tag);
}
function dispatchTagAdd() {
    const handler = window;
    if (handler.__onTagAdd)
        handler.__onTagAdd();
}
function dispatchSortSelect(sort) {
    const handler = window;
    if (handler.__onSortSelect)
        handler.__onSortSelect(sort);
}
function dispatchMoveToNotebook(nbId) {
    const handler = window;
    if (handler.__onMoveToNotebook)
        handler.__onMoveToNotebook(nbId);
}
function dispatchNotebookCreated(name) {
    const handler = window;
    if (handler.__onNotebookCreated)
        handler.__onNotebookCreated(name);
}
function dispatchNotebookCancelled() {
    const handler = window;
    if (handler.__onNotebookCancelled)
        handler.__onNotebookCancelled();
}
// ── Editor content helpers ─────────────────────────────────────────────
function getEditorContent() {
    return editorContent.innerHTML;
}
function setEditorContent(html) {
    editorContent.innerHTML = html;
}
function focusEditor() {
    editorContent.focus();
}
// ── Dropdown management ────────────────────────────────────────────────
function showMoveDropdown() {
    const state = (0, model_1.getState)();
    const btn = dom.btnMove;
    const rect = btn.getBoundingClientRect();
    dom.moveDropdown.style.top = (rect.bottom + 4) + 'px';
    dom.moveDropdown.style.left = rect.left + 'px';
    dom.moveDropdown.style.display = 'block';
    let html = '';
    for (const nb of state.notebooks) {
        html += `<div class="dropdown-item" data-move-to="${nb.id}">
      <span class="notebook-dot" style="background:${nb.color};display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px;"></span>
      ${escapeHtml(nb.name)}
    </div>`;
    }
    dom.moveDropdown.innerHTML = html;
}
function showSortDropdown() {
    const btn = dom.btnSort;
    const rect = btn.getBoundingClientRect();
    dom.sortDropdown.style.top = (rect.bottom + 4) + 'px';
    dom.sortDropdown.style.left = rect.left + 'px';
    dom.sortDropdown.style.display = 'block';
}
function showTrashMenu(x, y) {
    dom.trashMenu.style.top = y + 'px';
    dom.trashMenu.style.left = x + 'px';
    dom.trashMenu.style.display = 'block';
}
function hideAllDropdowns() {
    dom.moveDropdown.style.display = 'none';
    dom.sortDropdown.style.display = 'none';
    dom.trashMenu.style.display = 'none';
}
function showNewNotebookDialog() {
    dom.newNotebookDialog.style.display = 'flex';
    dom.newNotebookInput.value = '';
    dom.newNotebookInput.focus();
}
function hideNewNotebookDialog() {
    dom.newNotebookDialog.style.display = 'none';
}
// ── Toolbar state ──────────────────────────────────────────────────────
function updateToolbarState() {
    const toolbar = dom.editorToolbar;
    const buttons = toolbar.querySelectorAll('.toolbar-btn[data-cmd]');
    for (const btn of buttons) {
        const cmd = btn.dataset.cmd;
        if (!cmd)
            continue;
        let isActive = false;
        switch (cmd) {
            case 'bold':
                isActive = document.queryCommandState('bold');
                break;
            case 'italic':
                isActive = document.queryCommandState('italic');
                break;
            case 'underline':
                isActive = document.queryCommandState('underline');
                break;
            case 'strikethrough':
                isActive = document.queryCommandState('strikeThrough');
                break;
            case 'h1':
                isActive = document.queryCommandState('formatBlock') && getCurrentBlock() === 'H1';
                break;
            case 'h2':
                isActive = document.queryCommandState('formatBlock') && getCurrentBlock() === 'H2';
                break;
            case 'h3':
                isActive = document.queryCommandState('formatBlock') && getCurrentBlock() === 'H3';
                break;
            case 'ul':
                isActive = document.queryCommandState('insertUnorderedList');
                break;
            case 'ol':
                isActive = document.queryCommandState('insertOrderedList');
                break;
            case 'blockquote':
                isActive = getCurrentBlock() === 'BLOCKQUOTE';
                break;
            case 'code': {
                const sel = window.getSelection();
                if (sel && sel.rangeCount > 0) {
                    let node = sel.anchorNode;
                    while (node && node !== editorContent) {
                        if (node instanceof HTMLElement && node.tagName === 'CODE') {
                            isActive = true;
                            break;
                        }
                        node = node.parentNode;
                    }
                }
                break;
            }
        }
        btn.classList.toggle('active', isActive);
    }
}
function getCurrentBlock() {
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0)
        return '';
    let node = sel.anchorNode;
    while (node && node !== editorContent) {
        if (node instanceof HTMLElement && /^H[1-6]|BLOCKQUOTE$/.test(node.tagName)) {
            return node.tagName;
        }
        node = node.parentNode;
    }
    return '';
}
// ── Drag and drop for notes ────────────────────────────────────────────
function setupDragDrop() {
    // Note items are draggable
    dom.noteListContainer.addEventListener('dragstart', (e) => {
        const noteItem = e.target.closest('.note-item');
        if (!noteItem)
            return;
        const noteId = noteItem.dataset.noteId;
        if (noteId) {
            e.dataTransfer.setData('text/plain', noteId);
            noteItem.style.opacity = '0.5';
        }
    });
    dom.noteListContainer.addEventListener('dragend', (e) => {
        const noteItem = e.target.closest('.note-item');
        if (noteItem)
            noteItem.style.opacity = '';
    });
    // Sidebar notebooks as drop targets
    dom.sidebarNotebooks.addEventListener('dragover', (e) => {
        e.preventDefault();
        const item = e.target.closest('.sidebar-item');
        if (item)
            item.classList.add('drop-target');
    });
    dom.sidebarNotebooks.addEventListener('dragleave', (e) => {
        const item = e.target.closest('.sidebar-item');
        if (item)
            item.classList.remove('drop-target');
    });
    dom.sidebarNotebooks.addEventListener('drop', (e) => {
        e.preventDefault();
        const item = e.target.closest('.sidebar-item');
        if (item)
            item.classList.remove('drop-target');
        const noteId = e.dataTransfer.getData('text/plain');
        const nbId = item?.dataset.notebook;
        if (noteId && nbId) {
            dispatchMoveToNotebook(nbId);
        }
    });
}
};

// ── module: src/editor.ts ──
__mods["src/editor.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.initEditor = initEditor;
exports.handleEditorKeydown = handleEditorKeydown;
exports.handleToolbarCommand = handleToolbarCommand;
exports.loadNoteContent = loadNoteContent;
exports.clearEditor = clearEditor;
// ── Editor: contenteditable management, toolbar, keyboard shortcuts ─────
const constants_1 = require("./constants");
const model_1 = require("./model");
const renderer_1 = require("./renderer");
let saveTimeout = null;
let isComposing = false; // IME composition flag
let lastContent = '';
// ── Editor initialization ──────────────────────────────────────────────
function initEditor() {
    const editor = document.getElementById('editor-content');
    const titleInput = document.getElementById('editor-title');
    const tagInput = document.getElementById('tag-input');
    // Title input
    titleInput.addEventListener('input', () => {
        const state = (0, model_1.getState)();
        if (!state.activeNoteId)
            return;
        const note = (0, model_1.getNote)(state.activeNoteId);
        if (note && note.title !== titleInput.value) {
            (0, model_1.updateNote)(state.activeNoteId, { title: titleInput.value });
            scheduleSave();
        }
    });
    // Content input
    editor.addEventListener('input', () => {
        if (isComposing)
            return;
        const state = (0, model_1.getState)();
        if (!state.activeNoteId)
            return;
        const content = (0, model_1.sanitizeHtml)(editor.innerHTML);
        if (content !== lastContent) {
            (0, model_1.updateNote)(state.activeNoteId, { content });
            lastContent = content;
            scheduleSave();
        }
    });
    // Selection change → update toolbar
    editor.addEventListener('selectionchange', () => {
        // Use a microtask to let the selection settle
        requestAnimationFrame(() => (0, renderer_1.updateToolbarState)());
    });
    // Keyboard shortcuts
    editor.addEventListener('keydown', handleEditorKeydown);
    // Paste sanitization
    editor.addEventListener('paste', (e) => {
        e.preventDefault();
        const text = (e.clipboardData || window.clipboardData)?.getData('text/plain') || '';
        document.execCommand('insertText', false, text);
    });
    // Drag prevention (don't let files be dropped)
    editor.addEventListener('dragover', (e) => e.preventDefault());
    editor.addEventListener('drop', (e) => e.preventDefault());
    // IME composition
    editor.addEventListener('compositionstart', () => { isComposing = true; });
    editor.addEventListener('compositionend', () => {
        isComposing = false;
        // Trigger input after composition ends
        const state = (0, model_1.getState)();
        if (state.activeNoteId) {
            const content = (0, model_1.sanitizeHtml)(editor.innerHTML);
            if (content !== lastContent) {
                (0, model_1.updateNote)(state.activeNoteId, { content });
                lastContent = content;
                scheduleSave();
            }
        }
    });
    // Tag input
    tagInput.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') {
            e.preventDefault();
            addTagFromInput(tagInput);
        }
    });
    // Tag chip clicks
    document.getElementById('tags-container').addEventListener('click', (e) => {
        const removeBtn = e.target.closest('.tag-remove');
        if (removeBtn) {
            const tag = removeBtn.dataset.tag;
            if (tag)
                removeTag(tag);
            return;
        }
        const chip = e.target.closest('.tag-chip');
        if (chip) {
            const tag = chip.dataset.tag;
            if (tag)
                filterByTag(tag);
        }
    });
}
// ── Autosave ───────────────────────────────────────────────────────────
function scheduleSave() {
    if (saveTimeout)
        clearTimeout(saveTimeout);
    saveTimeout = setTimeout(() => {
        (0, renderer_1.showSavedIndicator)();
    }, constants_1.AUTOSAVE_DEBOUNCE);
}
// ── Markdown shortcuts ─────────────────────────────────────────────────
function handleEditorKeydown(e) {
    const editor = document.getElementById('editor-content');
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0)
        return;
    const key = e.key;
    // Handle Enter key for list continuation and markdown shortcuts
    if (key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.altKey) {
        if (handleEnterKey(editor, sel)) {
            e.preventDefault();
        }
        return;
    }
    // Handle Tab for indent/outdent
    if (key === 'Tab') {
        e.preventDefault();
        if (e.shiftKey) {
            document.execCommand('outdent', false);
        }
        else {
            document.execCommand('indent', false);
        }
        return;
    }
    // Markdown shortcuts: check on space key
    if (key === ' ' || key === 'Spacebar') {
        if (handleMarkdownShortcut(editor, sel)) {
            e.preventDefault();
        }
    }
}
function handleEnterKey(editor, sel) {
    const range = sel.getRangeAt(0);
    const anchorNode = range.startContainer;
    // Find the current block element
    let block = findBlockElement(anchorNode);
    if (!block)
        return false;
    // Check if we're in a list
    const listParent = findListParent(block);
    if (listParent) {
        const isEmpty = isListItemEmpty(block);
        if (isEmpty) {
            // Exit the list: remove the empty item
            const parentList = listParent;
            const prevSibling = block.previousElementSibling;
            parentList.removeChild(block);
            // If list is now empty, remove it too
            if (parentList.children.length === 0) {
                const p = document.createElement('p');
                p.textContent = '';
                parentList.parentNode?.replaceChild(p, parentList);
                // Place cursor in the new paragraph
                const newRange = document.createRange();
                newRange.setStart(p, 0);
                newRange.collapse(true);
                sel.removeAllRanges();
                sel.addRange(newRange);
            }
            else {
                // Place cursor after the previous sibling
                const newRange = document.createRange();
                if (prevSibling) {
                    newRange.setStartAfter(prevSibling);
                }
                else {
                    newRange.setStartBefore(parentList);
                }
                newRange.collapse(true);
                sel.removeAllRanges();
                sel.addRange(newRange);
            }
            return true; // Prevent default
        }
    }
    return false;
}
function findBlockElement(node) {
    const editor = document.getElementById('editor-content');
    while (node && node !== document.body && node !== editor) {
        if (node instanceof HTMLElement) {
            const tag = node.tagName;
            if (['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE', 'LI', 'DIV'].includes(tag)) {
                return node;
            }
        }
        node = node.parentNode;
    }
    return null;
}
function findListParent(element) {
    let parent = element.parentElement;
    while (parent) {
        if (parent.tagName === 'UL' || parent.tagName === 'OL')
            return parent;
        parent = parent.parentElement;
    }
    return null;
}
function isListItemEmpty(li) {
    // A list item is empty if it has no text content (ignoring whitespace)
    // and no child elements other than checkboxes
    const text = li.textContent?.trim() || '';
    if (text)
        return false;
    // Check for checklist checkbox - that's OK
    const children = li.children;
    for (let i = 0; i < children.length; i++) {
        if (children[i].classList?.contains('checklist-checkbox'))
            continue;
        return false;
    }
    return true;
}
// ── Markdown shortcuts on space ────────────────────────────────────────
function handleMarkdownShortcut(editor, sel) {
    const range = sel.getRangeAt(0);
    const anchorNode = range.startContainer;
    // Find the text node and its position
    if (anchorNode.nodeType !== Node.TEXT_NODE)
        return false;
    const text = anchorNode.textContent || '';
    const offset = range.startOffset;
    // Get the text before the cursor (the space hasn't been inserted yet on keydown)
    const beforeCursor = text.substring(0, offset);
    // Find the current line (text before the last newline)
    const lines = beforeCursor.split('\n');
    const currentLine = lines[lines.length - 1];
    // Check markdown patterns - the space triggers the conversion
    // At keydown time, the space hasn't been inserted yet
    // The line may have leading whitespace from the paragraph, so check trimmed
    const trimmedLine = currentLine.trim();
    let deleteLen = 0;
    let format = '';
    if (trimmedLine === '#') {
        format = 'H1';
        deleteLen = 1;
    }
    else if (trimmedLine === '##') {
        format = 'H2';
        deleteLen = 2;
    }
    else if (trimmedLine === '###') {
        format = 'H3';
        deleteLen = 3;
    }
    else if (trimmedLine === '-') {
        format = 'ul';
        deleteLen = 1;
    }
    else if (trimmedLine === '1.') {
        format = 'ol';
        deleteLen = 2;
    }
    else if (trimmedLine === '[]') {
        // Delete the "[]" prefix first, then apply checklist
        if (anchorNode instanceof Text && offset >= 2) {
            anchorNode.deleteData(offset - 2, 2);
        }
        // Re-establish selection after deletion
        const newRange = document.createRange();
        newRange.setStart(anchorNode, Math.max(0, offset - 2));
        newRange.collapse(true);
        sel.removeAllRanges();
        sel.addRange(newRange);
        applyChecklistItem(editor, sel);
        return true;
    }
    else if (trimmedLine === '>') {
        format = 'BLOCKQUOTE';
        deleteLen = 1;
    }
    if (format) {
        // Select and delete the prefix
        const newRange = document.createRange();
        newRange.setStart(anchorNode, offset - deleteLen);
        newRange.setEnd(anchorNode, offset);
        sel.removeAllRanges();
        sel.addRange(newRange);
        document.execCommand('delete', false);
        // Re-establish selection at the start of the text node
        const restoreRange = document.createRange();
        if (anchorNode.parentNode) {
            restoreRange.setStart(anchorNode, 0);
        }
        else {
            restoreRange.setStart(editor, 0);
        }
        restoreRange.collapse(true);
        sel.removeAllRanges();
        sel.addRange(restoreRange);
        editor.focus();
        applyBlockFormat(format, editor, sel);
        return true;
    }
    return false;
}
function deleteSelectedText(textNode, start, len) {
    if (textNode instanceof Text && start >= 0 && start + len <= textNode.length) {
        textNode.deleteData(start, len);
    }
}
function applyBlockFormat(format, editor, sel) {
    // Apply the format
    if (format === 'ul') {
        document.execCommand('insertUnorderedList', false);
    }
    else if (format === 'ol') {
        document.execCommand('insertOrderedList', false);
    }
    else {
        document.execCommand('formatBlock', false, format);
    }
    // Re-focus editor
    editor.focus();
}
function applyChecklistItem(editor, sel) {
    // Get the current block element
    let range;
    try {
        range = sel.getRangeAt(0);
    }
    catch {
        // Selection is invalid, create a new one at the start of editor
        range = document.createRange();
        range.setStart(editor, 0);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
    }
    let block = findBlockElement(range.startContainer);
    if (!block) {
        // Create a paragraph first
        document.execCommand('formatBlock', false, 'P');
        try {
            range = sel.getRangeAt(0);
        }
        catch {
            range = document.createRange();
            range.setStart(editor, 0);
            range.collapse(true);
            sel.removeAllRanges();
            sel.addRange(range);
        }
        block = findBlockElement(range.startContainer);
    }
    if (!block)
        return;
    // Convert to list item with checklist
    const parentList = findListParent(block);
    if (parentList) {
        // Already in a list, convert to checklist
        convertListToChecklist(parentList);
    }
    else {
        // Create a new ul and move the block into it
        const ul = document.createElement('ul');
        const li = document.createElement('li');
        li.className = 'checklist-item';
        // Add checkbox
        const checkbox = document.createElement('span');
        checkbox.className = 'checklist-checkbox';
        checkbox.addEventListener('click', (e) => {
            e.stopPropagation();
            checkbox.classList.toggle('checked');
            li.classList.toggle('checked-text');
            saveChecklistState();
        });
        li.appendChild(checkbox);
        // Move block content into li
        while (block.firstChild) {
            li.appendChild(block.firstChild);
        }
        ul.appendChild(li);
        // Replace the block with the ul
        const parent = block.parentNode;
        if (parent) {
            parent.replaceChild(ul, block);
        }
        else {
            // If no parent, append to editor
            editor.appendChild(ul);
        }
        // Restore cursor
        const newRange = document.createRange();
        newRange.selectNodeContents(li);
        newRange.collapse(false);
        sel.removeAllRanges();
        sel.addRange(newRange);
    }
}
function convertListToChecklist(ul) {
    const items = ul.querySelectorAll('li');
    items.forEach(li => {
        if (!li.classList.contains('checklist-item')) {
            li.className = 'checklist-item';
            const checkbox = document.createElement('span');
            checkbox.className = 'checklist-checkbox';
            checkbox.addEventListener('click', (e) => {
                e.stopPropagation();
                checkbox.classList.toggle('checked');
                li.classList.toggle('checked-text');
                saveChecklistState();
            });
            li.insertBefore(checkbox, li.firstChild);
        }
    });
}
function saveChecklistState() {
    const state = (0, model_1.getState)();
    if (!state.activeNoteId)
        return;
    const content = (0, renderer_1.getEditorContent)();
    (0, model_1.updateNote)(state.activeNoteId, { content });
    scheduleSave();
}
// ── Toolbar command handler ────────────────────────────────────────────
function handleToolbarCommand(cmd) {
    const editor = document.getElementById('editor-content');
    editor.focus();
    switch (cmd) {
        case 'bold':
            document.execCommand('bold', false);
            break;
        case 'italic':
            document.execCommand('italic', false);
            break;
        case 'underline':
            document.execCommand('underline', false);
            break;
        case 'strikethrough':
            document.execCommand('strikeThrough', false);
            break;
        case 'code':
            insertInlineCode();
            break;
        case 'h1':
            document.execCommand('formatBlock', false, 'H1');
            break;
        case 'h2':
            document.execCommand('formatBlock', false, 'H2');
            break;
        case 'h3':
            document.execCommand('formatBlock', false, 'H3');
            break;
        case 'ul':
            document.execCommand('insertUnorderedList', false);
            break;
        case 'ol':
            document.execCommand('insertOrderedList', false);
            break;
        case 'checklist':
            insertChecklistItem();
            break;
        case 'blockquote':
            document.execCommand('formatBlock', false, 'BLOCKQUOTE');
            break;
        case 'hr':
            document.execCommand('insertHorizontalRule', false);
            break;
        case 'undo':
            document.execCommand('undo', false);
            break;
        case 'redo':
            document.execCommand('redo', false);
            break;
    }
    (0, renderer_1.updateToolbarState)();
    // Save after command
    const state = (0, model_1.getState)();
    if (state.activeNoteId) {
        const content = (0, renderer_1.getEditorContent)();
        (0, model_1.updateNote)(state.activeNoteId, { content });
        scheduleSave();
    }
}
function insertInlineCode() {
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0)
        return;
    const range = sel.getRangeAt(0);
    const selectedText = range.toString();
    if (selectedText) {
        // Wrap selection in code tags
        const code = document.createElement('code');
        code.textContent = selectedText;
        range.deleteContents();
        range.insertNode(code);
        range.setStartAfter(code);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
    }
    else {
        // Insert code placeholder
        document.execCommand('insertText', false, '`code`');
    }
}
function insertChecklistItem() {
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0)
        return;
    const editor = document.getElementById('editor-content');
    applyChecklistItem(editor, sel);
}
// ── Tag management ─────────────────────────────────────────────────────
function addTagFromInput(input) {
    const state = (0, model_1.getState)();
    if (!state.activeNoteId)
        return;
    const tag = input.value.trim().toLowerCase().replace(/[^a-z0-9-]/g, '');
    if (!tag)
        return;
    const note = (0, model_1.getNote)(state.activeNoteId);
    if (!note)
        return;
    if (!note.tags.includes(tag)) {
        (0, model_1.updateNoteTags)(state.activeNoteId, [...note.tags, tag]);
    }
    input.value = '';
}
function removeTag(tag) {
    const state = (0, model_1.getState)();
    if (!state.activeNoteId)
        return;
    const note = (0, model_1.getNote)(state.activeNoteId);
    if (!note)
        return;
    (0, model_1.updateNoteTags)(state.activeNoteId, note.tags.filter(t => t !== tag));
}
function filterByTag(tag) {
    const handler = window;
    if (handler.__onTagClick)
        handler.__onTagClick(tag);
}
// ── Load note content into editor ──────────────────────────────────────
function loadNoteContent(noteId) {
    const note = (0, model_1.getNote)(noteId);
    if (!note)
        return;
    lastContent = note.content;
    (0, renderer_1.setEditorContent)(note.content);
    // Re-bind checklist checkboxes
    rebindCheckboxes();
}
function rebindCheckboxes() {
    const editor = document.getElementById('editor-content');
    const checkboxes = editor.querySelectorAll('.checklist-checkbox');
    checkboxes.forEach(cb => {
        // Remove old listeners by cloning
        const newCb = cb.cloneNode(true);
        cb.parentNode?.replaceChild(newCb, cb);
        newCb.addEventListener('click', (e) => {
            e.stopPropagation();
            newCb.classList.toggle('checked');
            const li = newCb.closest('li');
            if (li)
                li.classList.toggle('checked-text');
            saveChecklistState();
        });
    });
}
// ── Clear editor ───────────────────────────────────────────────────────
function clearEditor() {
    const editor = document.getElementById('editor-content');
    editor.innerHTML = '';
    lastContent = '';
}
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>