← Notes results

Notes

Qwen 3.6 27b MTP · 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 90,955 bytes · SHA-256 bfe672bb2c49
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Notes — Your Personal Knowledge Base</title>
<style>
/* ── css: layout ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  overflow: hidden;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-primary);
  background: var(--bg);
}

#app {
  display: flex;
  height: 100vh;
  width: 100vw;
  overflow: hidden;
}

/* ─── CSS Custom Properties (Light Theme) ─── */
:root, [data-theme="light"] {
  --bg: #FAF9F7;
  --bg-secondary: #FFFFFF;
  --bg-tertiary: #F3F1EE;
  --bg-hover: #EDEBE8;
  --bg-active: #E5E2DD;
  --text-primary: #1C1B1A;
  --text-secondary: #6B6760;
  --text-tertiary: #9B9790;
  --accent: #D4883E;
  --accent-hover: #C47A32;
  --accent-light: rgba(212, 136, 62, 0.1);
  --border: #E5E2DD;
  --border-light: #EDEBE8;
  --danger: #D94F4F;
  --danger-hover: #C74444;
  --success: #4CAF50;
  --tag-bg: rgba(212, 136, 62, 0.1);
  --tag-text: #B8722E;
  --editor-bg: #FFFFFF;
  --shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
  --shadow-lg: 0 4px 12px rgba(0,0,0,0.08);
}

[data-theme="dark"] {
  --bg: #1A1917;
  --bg-secondary: #232220;
  --bg-tertiary: #2C2B28;
  --bg-hover: #353430;
  --bg-active: #3D3C38;
  --text-primary: #E8E6E1;
  --text-secondary: #9B9790;
  --text-tertiary: #6B6760;
  --accent: #E8A54F;
  --accent-hover: #D4953F;
  --accent-light: rgba(232, 165, 79, 0.12);
  --border: #353430;
  --border-light: #2C2B28;
  --danger: #E06060;
  --danger-hover: #D04F4F;
  --success: #6BC970;
  --tag-bg: rgba(232, 165, 79, 0.12);
  --tag-text: #E8A54F;
  --editor-bg: #232220;
  --shadow: 0 1px 3px rgba(0,0,0,0.2), 0 1px 2px rgba(0,0,0,0.15);
  --shadow-lg: 0 4px 12px rgba(0,0,0,0.3);
}

/* ─── Resize Handles ─── */
.resize-handle {
  width: 4px;
  cursor: col-resize;
  background: transparent;
  transition: background 0.15s;
  flex-shrink: 0;
  z-index: 10;
}

.resize-handle:hover, .resize-handle.active {
  background: var(--accent);
}

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

/* ─── Selection ─── */
::selection { background: var(--accent-light); }

/* ─── Focus Ring ─── */
:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }

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

/* ─── Utility Classes ─── */
.hidden { display: none !important; }
.fade-in { animation: fadeIn 0.2s ease-out; }
/* ── css: sidebar ── */
/* ─── Sidebar Pane ─── */
#sidebar-pane {
  width: var(--sidebar-width, 240px);
  min-width: 180px;
  max-width: 400px;
  background: var(--bg-secondary);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: width 0.2s ease, min-width 0.2s ease;
}

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

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

.sidebar-title {
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-secondary);
}

.sidebar-actions {
  display: flex;
  gap: 2px;
}

.sidebar-btn {
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  transition: all 0.15s;
}

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

/* ─── Sidebar Search ─── */
.sidebar-search {
  padding: 4px 12px 8px;
  flex-shrink: 0;
}

.sidebar-search input {
  width: 100%;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0 10px 0 32px;
  font-size: 13px;
  background: var(--bg-tertiary) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%239B9790' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E") 10px center no-repeat;
  color: var(--text-primary);
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}

.sidebar-search input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-light);
}

.sidebar-search input::placeholder {
  color: var(--text-tertiary);
}

/* ─── Sidebar Navigation ─── */
.sidebar-nav {
  flex: 1;
  overflow-y: auto;
  padding: 4px 8px;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text-secondary);
  transition: all 0.12s;
  user-select: none;
  position: relative;
}

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

.nav-item.active {
  background: var(--accent-light);
  color: var(--accent);
  font-weight: 600;
}

.nav-icon {
  width: 18px;
  text-align: center;
  flex-shrink: 0;
  font-size: 15px;
}

.nav-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-count {
  font-size: 11px;
  color: var(--text-tertiary);
  min-width: 20px;
  text-align: right;
}

/* ─── Notebook Items ─── */
.notebook-item {
  margin-top: 4px;
}

.notebook-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text-secondary);
  transition: all 0.12s;
  user-select: none;
}

.notebook-header:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.notebook-header.active {
  background: var(--accent-light);
  color: var(--accent);
  font-weight: 600;
}

.notebook-chevron {
  width: 14px;
  text-align: center;
  flex-shrink: 0;
  transition: transform 0.2s;
  font-size: 10px;
}

.notebook-header.collapsed .notebook-chevron {
  transform: rotate(-90deg);
}

.notebook-notes {
  overflow: hidden;
  max-height: 1000px;
  transition: max-height 0.25s ease, opacity 0.2s;
  opacity: 1;
}

.notebook-notes.collapsed {
  max-height: 0 !important;
  opacity: 0;
}

.notebook-note-item {
  padding-left: 34px;
}

/* ─── Sidebar Footer ─── */
.sidebar-footer {
  padding: 8px 12px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

.add-notebook-btn, .add-note-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 7px 10px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: 6px;
  font-size: 13px;
  transition: all 0.12s;
}

.add-notebook-btn:hover, .add-note-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

/* ─── Context Menu ─── */
.context-menu {
  position: fixed;
  z-index: 1000;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow-lg);
  padding: 4px;
  min-width: 160px;
  animation: fadeIn 0.12s ease-out;
}

.context-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  border-radius: 5px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text-primary);
  transition: background 0.1s;
}

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

.context-menu-item.danger {
  color: var(--danger);
}

.context-menu-divider {
  height: 1px;
  background: var(--border-light);
  margin: 4px 8px;
}

/* ─── Tag Filter Bar ─── */
.tag-filter-bar {
  display: flex;
  gap: 6px;
  padding: 6px 12px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--border-light);
}

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

.tag-chip:hover {
  opacity: 0.8;
}

.tag-chip.active {
  background: var(--accent);
  color: white;
}
/* ── css: note-list ── */
/* ─── Note List Pane ─── */
#note-list-pane {
  width: var(--notelist-width, 300px);
  min-width: 200px;
  max-width: 500px;
  background: var(--bg-secondary);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.notelist-header {
  padding: 14px 16px 8px;
  flex-shrink: 0;
  border-bottom: 1px solid var(--border-light);
}

.notelist-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}

.notelist-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text-primary);
}

.notelist-actions {
  display: flex;
  gap: 2px;
}

.sort-btn {
  padding: 4px 8px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  border-radius: 4px;
  font-size: 12px;
  transition: all 0.15s;
}

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

/* ─── Note List Items ─── */
.note-list-container {
  flex: 1;
  overflow-y: auto;
  padding: 4px 8px;
}

.note-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 12px;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.15s;
  position: relative;
}

.note-item:hover {
  background: var(--bg-hover);
}

.note-item.active {
  background: var(--accent-light);
}

.note-item-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.note-item.active .note-item-title {
  color: var(--accent);
}

.note-item-snippet {
  font-size: 12px;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.4;
}

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

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

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

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

.note-tag:hover {
  opacity: 0.7;
}

/* ─── Search Highlight ─── */
.search-highlight {
  background: rgba(212, 136, 62, 0.25);
  border-radius: 2px;
  padding: 0 1px;
}

[data-theme="dark"] .search-highlight {
  background: rgba(232, 165, 79, 0.3);
}

/* ─── Empty State ─── */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  text-align: center;
  color: var(--text-tertiary);
}

.empty-state-icon {
  font-size: 36px;
  margin-bottom: 12px;
  opacity: 0.5;
}

.empty-state-text {
  font-size: 14px;
  line-height: 1.5;
}

/* ─── Note Actions Dropdown ─── */
.note-actions-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 24px;
  height: 24px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: all 0.15s;
}

.note-item:hover .note-actions-btn {
  opacity: 1;
}

.note-actions-btn:hover {
  background: var(--bg-active);
  color: var(--text-primary);
}
/* ── css: editor ── */
/* ─── Editor Pane ─── */
#editor-pane {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg);
}

/* ─── Toolbar ─── */
#toolbar {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-secondary);
  flex-shrink: 0;
  overflow-x: auto;
}

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

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

.toolbar-btn.active {
  background: var(--accent-light);
  color: var(--accent);
}

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

/* ─── Editor Header (Title + Tags) ─── */
#editor-header {
  padding: 16px 32px 8px;
  flex-shrink: 0;
}

.editor-title-input {
  width: 100%;
  border: none;
  background: transparent;
  font-size: 28px;
  font-weight: 700;
  color: var(--text-primary);
  outline: none;
  font-family: inherit;
  line-height: 1.3;
}

.editor-title-input::placeholder {
  color: var(--text-tertiary);
}

.editor-tags-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  flex-wrap: wrap;
}

.editor-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  background: var(--tag-bg);
  color: var(--tag-text);
  border-radius: 10px;
  font-size: 12px;
}

.editor-tag-remove {
  cursor: pointer;
  opacity: 0.6;
  font-size: 14px;
  line-height: 1;
}

.editor-tag-remove:hover {
  opacity: 1;
}

.add-tag-input {
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: 12px;
  outline: none;
  width: 80px;
  padding: 2px 4px;
}

.add-tag-input::placeholder {
  color: var(--text-tertiary);
}

/* ─── Editor Content ─── */
#editor-content {
  flex: 1;
  overflow-y: auto;
  padding: 8px 32px 60px;
  font-family: 'Georgia', 'Noto Serif', 'Source Serif Pro', serif;
  font-size: 16px;
  line-height: 1.75;
  color: var(--text-primary);
  outline: none;
}

#editor-content:empty::before {
  content: attr(data-placeholder);
  color: var(--text-tertiary);
  pointer-events: none;
}

/* ─── Editor Content Typography ─── */
#editor-content h1 { font-size: 2em; font-weight: 700; margin: 0.6em 0 0.3em; line-height: 1.3; }
#editor-content h2 { font-size: 1.5em; font-weight: 700; margin: 0.5em 0 0.25em; line-height: 1.35; }
#editor-content h3 { font-size: 1.25em; font-weight: 600; margin: 0.4em 0 0.2em; line-height: 1.4; }

#editor-content blockquote {
  border-left: 3px solid var(--accent);
  padding-left: 16px;
  margin: 0.5em 0;
  color: var(--text-secondary);
  font-style: italic;
}

#editor-content code {
  background: var(--bg-tertiary);
  padding: 2px 5px;
  border-radius: 3px;
  font-family: 'SF Mono', 'Fira Code', monospace;
  font-size: 0.9em;
}

#editor-content hr {
  border: none;
  height: 1px;
  background: var(--border);
  margin: 1.5em 0;
}

/* ─── Lists in Editor ─── */
#editor-content ul, #editor-content ol {
  padding-left: 24px;
  margin: 0.3em 0;
}

#editor-content li {
  margin-bottom: 2px;
}

/* ─── Checklist Items ─── */
.checklist-item {
  list-style: none !important;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding-left: 0 !important;
}

.checklist-checkbox {
  width: 16px;
  height: 16px;
  border: 2px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  flex-shrink: 0;
  margin-top: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s;
}

.checklist-checkbox.checked {
  background: var(--accent);
  border-color: var(--accent);
}

.checklist-checkbox.checked::after {
  content: '✓';
  color: white;
  font-size: 11px;
  line-height: 1;
}

.checklist-item.checked .checklist-text {
  text-decoration: line-through;
  color: var(--text-tertiary);
}

/* ─── Saved Indicator ─── */
#saved-indicator {
  position: fixed;
  bottom: 16px;
  right: 24px;
  padding: 6px 14px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 20px;
  font-size: 12px;
  color: var(--text-tertiary);
  box-shadow: var(--shadow);
  opacity: 0;
  transform: translateY(8px);
  transition: all 0.3s ease;
  pointer-events: none;
  z-index: 100;
}

#saved-indicator.visible {
  opacity: 1;
  transform: translateY(0);
}

/* ─── Editor Empty State ─── */
.editor-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--text-tertiary);
}

.editor-empty-icon {
  font-size: 48px;
  margin-bottom: 16px;
  opacity: 0.3;
}

.editor-empty-text {
  font-size: 16px;
  color: var(--text-tertiary);
}

/* ─── Move Menu ─── */
.move-menu {
  position: fixed;
  z-index: 1000;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow-lg);
  padding: 4px;
  min-width: 180px;
  animation: fadeIn 0.12s ease-out;
}

.move-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  border-radius: 5px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text-primary);
  transition: background 0.1s;
}

.move-menu-item:hover {
  background: var(--bg-hover);
}

/* ─── Drag Over Highlight ─── */
.drag-over {
  outline: 2px dashed var(--accent) !important;
  outline-offset: -2px;
}
</style>
</head>
<body>
<div id="sidebar-pane"></div>
<div id="sidebar-resize-handle" class="resize-handle vertical"></div>
<div id="note-list-pane"></div>
<div id="notelist-resize-handle" class="resize-handle vertical"></div>
<div id="editor-pane">
  <div id="toolbar"></div>
  <div id="editor-header"></div>
  <div id="editor-content" contenteditable="true" spellcheck="true"></div>
  <div id="saved-indicator"></div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./types":"src/types.ts","./state":"src/state.ts","./editor":"src/editor.ts","./ui":"src/ui.ts"},"src/state.ts":{"./types":"src/types.ts"},"src/editor.ts":{"./types":"src/types.ts","./state":"src/state.ts"},"src/ui.ts":{"./types":"src/types.ts","./state":"src/state.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";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const State = __importStar(require("./state"));
const editor_1 = require("./editor");
const ui_1 = require("./ui");
// ─── Initialize ──────────────────────────────────────────────────────
State.initState();
const editor = new editor_1.Editor({
    onTitleChange: () => { }, // handled inline via updateNote
    onSave: () => ui.renderSidebar(), // re-render sidebar counts after save
});
const ui = new ui_1.AppUI();
// ─── Apply Initial Layout & Theme ────────────────────────────────────
ui.applyLayout();
// ─── Load Selected Note ──────────────────────────────────────────────
function loadSelectedNote() {
    const note = State.getSelectedNote();
    if (note) {
        editor.loadNote(note);
    }
    else {
        editor.clear();
        ui.showEditorEmptyState();
    }
}
// ─── Subscribe to State Changes ──────────────────────────────────────
State.subscribe(() => {
    const s = State.getState();
    // Apply layout changes (theme, pane sizes)
    ui.applyLayout();
    // Re-render sidebar (notebook counts change)
    ui.renderSidebar();
    // Re-render note list when filters/sort/view change
    ui.renderNoteList();
    // Load selected note into editor
    loadSelectedNote();
});
// ─── Wire Up Toolbar Actions in Editor Pane ──────────────────────────
const editorPane = document.getElementById('editor-pane');
editorPane.addEventListener('toolbar-action', (e) => {
    const detail = e.detail;
    if (detail.heading) {
        const level = parseInt(detail.heading.replace('h', ''), 10);
        editor.applyHeading(level);
    }
    else if (detail.cmd === 'b') {
        document.execCommand('bold');
    }
    else if (detail.cmd === 'i') {
        document.execCommand('italic');
    }
    else if (detail.cmd === 'u') {
        document.execCommand('underline');
    }
    else if (detail.cmd === 's') {
        document.execCommand('strikethrough');
    }
    else if (detail.cmd === 'code') {
        editor.applyInlineCode();
    }
    else if (detail.cmd === 'blockquote') {
        editor.applyBlockquote();
    }
    else if (detail.cmd === 'hr') {
        editor.applyHorizontalRule();
    }
    // Re-focus editor after toolbar action
    const contentEl = document.getElementById('editor-content');
    if (contentEl)
        contentEl.focus();
});
// ─── Sidebar Drag-and-Drop for Notebooks ──────────────────────────────
const sidebarPane = document.getElementById('sidebar-pane');
sidebarPane.addEventListener('dragover', (e) => {
    e.preventDefault();
    const nbItem = e.target.closest('.notebook-item, .nav-item[data-id="all"]');
    if (nbItem) {
        nbItem.classList.add('drag-over');
    }
});
sidebarPane.addEventListener('dragleave', (e) => {
    const nbItem = e.target.closest('.notebook-item, .nav-item[data-id="all"]');
    if (nbItem)
        nbItem.classList.remove('drag-over');
});
sidebarPane.addEventListener('drop', (e) => {
    e.preventDefault();
    const noteId = e.dataTransfer.getData('text/plain');
    if (!noteId)
        return;
    // Find target notebook
    const nbHeader = e.target.closest('.notebook-header');
    if (nbHeader) {
        const targetNbId = nbHeader.getAttribute('data-id');
        if (targetNbId && targetNbId !== 'trash' && targetNbId !== 'all') {
            State.moveNoteToNotebook(noteId, targetNbId);
        }
    }
    // Clean up drag-over highlights
    sidebarPane.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
});
// ─── Keyboard Shortcuts (Global) ──────────────────────────────────────
document.addEventListener('keydown', (e) => {
    // Ctrl/Cmd + N — new note
    if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'n') {
        e.preventDefault();
        const s = State.getState();
        let targetNb;
        if (s.selectedNotebookId !== 'all' && s.selectedNotebookId !== 'trash') {
            targetNb = s.selectedNotebookId;
        }
        else if (s.notebooks.length > 0) {
            targetNb = s.notebooks[0].id;
        }
        State.createNote(targetNb);
    }
    // Ctrl/Cmd + F — focus search
    if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'f') {
        const contentEl = document.getElementById('editor-content');
        const sel = window.getSelection();
        // Only prevent default if there's no text selection in the editor
        if (!sel || sel.isCollapsed || !contentEl?.contains(sel.anchorNode)) {
            e.preventDefault();
            const searchInput = document.getElementById('search-input');
            if (searchInput)
                searchInput.focus();
        }
    }
    // Escape — clear tag filter or deselect note
    if (e.key === 'Escape') {
        const s = State.getState();
        if (s.tagFilter) {
            State.setTagFilter(null);
        }
        else if (s.searchQuery) {
            State.setSearchQuery('');
        }
    }
});
// ─── Initial Render ──────────────────────────────────────────────────
ui.renderSidebar();
ui.renderNoteList();
loadSelectedNote();
};

// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CONSTANTS = void 0;
// ─── Tunable Constants ────────────────────────────────────────────────
exports.CONSTANTS = {
    // Colors — warm light theme
    colors: {
        bg: '#FAF9F7',
        bgSecondary: '#FFFFFF',
        bgTertiary: '#F3F1EE',
        bgHover: '#EDEBE8',
        bgActive: '#E5E2DD',
        textPrimary: '#1C1B1A',
        textSecondary: '#6B6760',
        textTertiary: '#9B9790',
        accent: '#D4883E',
        accentHover: '#C47A32',
        accentLight: 'rgba(212, 136, 62, 0.1)',
        border: '#E5E2DD',
        borderLight: '#EDEBE8',
        danger: '#D94F4F',
        dangerHover: '#C74444',
        success: '#4CAF50',
        tagBg: 'rgba(212, 136, 62, 0.1)',
        tagText: '#B8722E',
        editorBg: '#FFFFFF',
        shadow: '0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)',
        shadowLg: '0 4px 12px rgba(0,0,0,0.08)',
    },
    // Dark theme colors
    darkColors: {
        bg: '#1A1917',
        bgSecondary: '#232220',
        bgTertiary: '#2C2B28',
        bgHover: '#353430',
        bgActive: '#3D3C38',
        textPrimary: '#E8E6E1',
        textSecondary: '#9B9790',
        textTertiary: '#6B6760',
        accent: '#E8A54F',
        accentHover: '#D4953F',
        accentLight: 'rgba(232, 165, 79, 0.12)',
        border: '#353430',
        borderLight: '#2C2B28',
        danger: '#E06060',
        dangerHover: '#D04F4F',
        success: '#6BC970',
        tagBg: 'rgba(232, 165, 79, 0.12)',
        tagText: '#E8A54F',
        editorBg: '#232220',
        shadow: '0 1px 3px rgba(0,0,0,0.2), 0 1px 2px rgba(0,0,0,0.15)',
        shadowLg: '0 4px 12px rgba(0,0,0,0.3)',
    },
    // Fonts
    fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
    editorFontFamily: "'Georgia', 'Noto Serif', 'Source Serif Pro', serif",
    fontSizeSm: '12px',
    fontSizeBase: '14px',
    fontSizeLg: '16px',
    // Layout
    sidebarMinWidth: 200,
    sidebarMaxWidth: 400,
    noteListMinWidth: 220,
    noteListMaxWidth: 500,
    defaultSidebarWidth: 240,
    defaultNoteListWidth: 300,
    // Behavior
    autosaveDebounceMs: 800,
    snippetLength: 120,
    maxTitleLength: 200,
};
};

// ── module: src/state.ts ──
__mods["src/state.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadState = loadState;
exports.saveState = saveState;
exports.getState = getState;
exports.setState = setState;
exports.updateState = updateState;
exports.subscribe = subscribe;
exports.genId = genId;
exports.createNotebook = createNotebook;
exports.renameNotebook = renameNotebook;
exports.deleteNotebook = deleteNotebook;
exports.toggleNotebookCollapse = toggleNotebookCollapse;
exports.createNote = createNote;
exports.updateNote = updateNote;
exports.togglePin = togglePin;
exports.duplicateNote = duplicateNote;
exports.moveToTrash = moveToTrash;
exports.restoreFromTrash = restoreFromTrash;
exports.permanentlyDelete = permanentlyDelete;
exports.moveNoteToNotebook = moveNoteToNotebook;
exports.selectNotebook = selectNotebook;
exports.selectNote = selectNote;
exports.setSortMode = setSortMode;
exports.setSearchQuery = setSearchQuery;
exports.setTagFilter = setTagFilter;
exports.toggleDarkTheme = toggleDarkTheme;
exports.setPaneLayout = setPaneLayout;
exports.getFilteredNotes = getFilteredNotes;
exports.getNoteCountForNotebook = getNoteCountForNotebook;
exports.getSelectedNote = getSelectedNote;
exports.extractPlainText = extractPlainText;
exports.extractSnippet = extractSnippet;
exports.sanitizeHTML = sanitizeHTML;
exports.initState = initState;
const types_1 = require("./types");
const STORAGE_KEY = 'notes-app-state-v1';
// ─── Default State ────────────────────────────────────────────────────
function createDefaultState() {
    const now = Date.now();
    return {
        notebooks: [
            { id: 'nb-personal', name: 'Personal', collapsed: false },
            { id: 'nb-work', name: 'Work', collapsed: false },
            { id: 'nb-ideas', name: 'Ideas', collapsed: false },
        ],
        notes: [
            {
                id: 'note-welcome',
                title: 'Welcome to Notes',
                content: '<p>This is your new <b>notes application</b>. Here are some things you can try:</p><ul><li data-list="bullet"><span>Create a checklist item by typing <code>[] </code></span></li><li data-list="bullet"><span>Format text with <b>bold</b>, <i>italic</i>, or <u>underline</u></span></li><li data-list="bullet"><span>Type <code># </code> for headings, <code>> </code> for blockquotes</span></li></ul><p>Your notes are saved automatically to your browser.</p>',
                notebookId: 'nb-personal',
                tags: ['welcome', 'getting-started'],
                pinned: true,
                createdAt: now,
                updatedAt: now,
            },
        ],
        selectedNotebookId: 'all',
        selectedNoteId: 'note-welcome',
        sortMode: 'modified',
        searchQuery: '',
        tagFilter: null,
        darkTheme: false,
        paneLayout: {
            sidebarWidth: types_1.CONSTANTS.defaultSidebarWidth,
            noteListWidth: types_1.CONSTANTS.defaultNoteListWidth,
            sidebarCollapsed: false,
        },
    };
}
// ─── Persistence ──────────────────────────────────────────────────────
function loadState() {
    try {
        const raw = localStorage.getItem(STORAGE_KEY);
        if (raw) {
            const parsed = JSON.parse(raw);
            // Merge with defaults for forward compatibility
            return { ...createDefaultState(), ...parsed };
        }
    }
    catch {
        // Corrupted storage — start fresh
    }
    return createDefaultState();
}
function saveState(state) {
    try {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
    }
    catch (e) {
        console.warn('Failed to save state:', e);
    }
}
// ─── State Mutators ───────────────────────────────────────────────────
let _state = null;
const listeners = new Set();
function getState() {
    if (!_state)
        throw new Error('State not initialized');
    return _state;
}
function setState(state) {
    _state = state;
    notifyListeners();
}
function updateState(updater) {
    if (!_state)
        throw new Error('State not initialized');
    const next = updater(_state);
    _state = next;
    saveState(next);
    notifyListeners();
}
function subscribe(fn) {
    listeners.add(fn);
    return () => listeners.delete(fn);
}
function notifyListeners() {
    for (const fn of listeners) {
        try {
            fn();
        }
        catch (e) {
            console.error('Listener error:', e);
        }
    }
}
// ─── ID Generation ────────────────────────────────────────────────────
let _idCounter = Date.now();
function genId() {
    return `id-${++_idCounter}-${Math.random().toString(36).slice(2, 8)}`;
}
// ─── Notebook Operations ──────────────────────────────────────────────
function createNotebook(name) {
    updateState(s => ({
        ...s,
        notebooks: [...s.notebooks, { id: genId(), name, collapsed: false }],
    }));
}
function renameNotebook(id, name) {
    updateState(s => ({
        ...s,
        notebooks: s.notebooks.map(nb => nb.id === id ? { ...nb, name } : nb),
    }));
}
function deleteNotebook(id) {
    updateState(s => {
        const notesInNotebook = s.notes.filter(n => n.notebookId === id);
        return {
            ...s,
            notebooks: s.notebooks.filter(nb => nb.id !== id),
            notes: [
                ...notesInNotebook.map(n => ({
                    ...n,
                    notebookId: 'trash',
                    originalNotebookId: n.notebookId,
                    updatedAt: Date.now(),
                })),
                ...s.notes.filter(n => n.notebookId !== id),
            ],
            selectedNotebookId: s.selectedNotebookId === id ? 'all' : s.selectedNotebookId,
        };
    });
}
function toggleNotebookCollapse(id) {
    updateState(s => ({
        ...s,
        notebooks: s.notebooks.map(nb => nb.id === id ? { ...nb, collapsed: !nb.collapsed } : nb),
    }));
}
// ─── Note Operations ──────────────────────────────────────────────────
function createNote(notebookId) {
    const now = Date.now();
    const note = {
        id: genId(),
        title: '',
        content: '<p><br></p>',
        notebookId: (notebookId || 'nb-personal'),
        tags: [],
        pinned: false,
        createdAt: now,
        updatedAt: now,
    };
    updateState(s => ({
        ...s,
        notes: [note, ...s.notes],
        selectedNotebookId: s.selectedNotebookId === 'trash' ? note.notebookId : s.selectedNotebookId,
        selectedNoteId: note.id,
    }));
    return note;
}
function updateNote(id, updates) {
    const now = Date.now();
    updateState(s => ({
        ...s,
        notes: s.notes.map(n => n.id === id ? { ...n, ...updates, updatedAt: now } : n),
    }));
}
function togglePin(id) {
    updateState(s => ({
        ...s,
        notes: s.notes.map(n => n.id === id ? { ...n, pinned: !n.pinned, updatedAt: Date.now() } : n),
    }));
}
function duplicateNote(id) {
    const now = Date.now();
    updateState(s => {
        const orig = s.notes.find(n => n.id === id);
        if (!orig)
            return s;
        const dup = {
            ...orig,
            id: genId(),
            title: `${orig.title} (copy)`,
            pinned: false,
            createdAt: now,
            updatedAt: now,
        };
        return { ...s, notes: [dup, ...s.notes], selectedNoteId: dup.id };
    });
}
function moveToTrash(id) {
    updateState(s => ({
        ...s,
        notes: s.notes.map(n => n.id === id ? {
            ...n, notebookId: 'trash', originalNotebookId: (n.notebookId !== 'trash' ? n.notebookId : undefined), updatedAt: Date.now()
        } : n),
    }));
}
function restoreFromTrash(id) {
    updateState(s => ({
        ...s,
        notes: s.notes.map(n => n.id === id ? {
            ...n, notebookId: (n.originalNotebookId || 'nb-personal'), updatedAt: Date.now()
        } : n),
    }));
}
function permanentlyDelete(id) {
    updateState(s => ({
        ...s,
        notes: s.notes.filter(n => n.id !== id),
        selectedNoteId: s.selectedNoteId === id ? null : s.selectedNoteId,
    }));
}
function moveNoteToNotebook(noteId, targetNotebookId) {
    updateState(s => ({
        ...s,
        notes: s.notes.map(n => n.id === noteId ? {
            ...n, notebookId: targetNotebookId, updatedAt: Date.now()
        } : n),
    }));
}
// ─── Selection & UI State ─────────────────────────────────────────────
function selectNotebook(id) {
    updateState(s => ({ ...s, selectedNotebookId: id }));
}
function selectNote(id) {
    updateState(s => ({ ...s, selectedNoteId: id }));
}
function setSortMode(mode) {
    updateState(s => ({ ...s, sortMode: mode }));
}
function setSearchQuery(query) {
    updateState(s => ({ ...s, searchQuery: query }));
}
function setTagFilter(tag) {
    updateState(s => ({ ...s, tagFilter: tag }));
}
function toggleDarkTheme() {
    updateState(s => ({ ...s, darkTheme: !s.darkTheme }));
}
// ─── Pane Layout ──────────────────────────────────────────────────────
function setPaneLayout(layout) {
    updateState(s => ({ ...s, paneLayout: { ...s.paneLayout, ...layout } }));
}
// ─── Derived Data ─────────────────────────────────────────────────────
function getFilteredNotes() {
    const s = getState();
    let notes = s.notes;
    // Filter by notebook view
    if (s.selectedNotebookId === 'trash') {
        notes = notes.filter(n => n.notebookId === 'trash');
    }
    else if (s.selectedNotebookId !== 'all') {
        notes = notes.filter(n => n.notebookId === s.selectedNotebookId);
    }
    // Filter by tag
    if (s.tagFilter) {
        const tf = s.tagFilter;
        notes = notes.filter(n => n.tags.includes(tf));
    }
    // Search filter
    if (s.searchQuery.trim()) {
        const q = s.searchQuery.toLowerCase().trim();
        notes = notes.filter(n => {
            const titleMatch = n.title.toLowerCase().includes(q);
            const bodyMatch = extractPlainText(n.content).toLowerCase().includes(q);
            return titleMatch || bodyMatch;
        });
    }
    // Sort: pinned first, then by sort mode
    notes.sort((a, b) => {
        if (a.pinned !== b.pinned)
            return a.pinned ? -1 : 1;
        switch (s.sortMode) {
            case 'created': return b.createdAt - a.createdAt;
            case 'alphabetical': return a.title.localeCompare(b.title);
            default: return b.updatedAt - a.updatedAt;
        }
    });
    return notes;
}
function getNoteCountForNotebook(notebookId) {
    const s = getState();
    if (notebookId === 'all')
        return s.notes.filter(n => n.notebookId !== 'trash').length;
    if (notebookId === 'trash')
        return s.notes.filter(n => n.notebookId === 'trash').length;
    return s.notes.filter(n => n.notebookId === notebookId).length;
}
function getSelectedNote() {
    const s = getState();
    if (!s.selectedNoteId)
        return null;
    return s.notes.find(n => n.id === s.selectedNoteId) || null;
}
// ─── Text Extraction (for search & snippets) ──────────────────────────
function extractPlainText(html) {
    const div = document.createElement('div');
    div.innerHTML = html;
    // Remove script/style tags entirely before extracting text
    div.querySelectorAll('script, style').forEach(el => el.remove());
    return (div.textContent || '').trim();
}
function extractSnippet(html, maxLength) {
    const plain = extractPlainText(html);
    if (!plain)
        return '';
    if (plain.length <= maxLength)
        return plain;
    // Try to break at a word boundary
    let truncated = plain.slice(0, maxLength);
    const lastSpace = truncated.lastIndexOf(' ');
    if (lastSpace > maxLength * 0.6) {
        truncated = truncated.slice(0, lastSpace);
    }
    return truncated + '…';
}
// ─── HTML Sanitization ────────────────────────────────────────────────
const ALLOWED_TAGS = new Set([
    'p', 'br', 'b', 'strong', 'i', 'em', 'u', 's', 'strike', 'del', 'code',
    'h1', 'h2', 'h3', 'blockquote', 'hr', 'ul', 'ol', 'li', 'span', 'div',
]);
const DANGEROUS_TAGS = new Set([
    'script', 'style', 'iframe', 'object', 'embed', 'form', 'input',
    'button', 'select', 'textarea', 'link', 'meta', 'base',
]);
const ALLOWED_ATTRS = new Set([
    'data-list', 'data-checked', 'class', 'style',
]);
function sanitizeHTML(html) {
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    for (const node of Array.from(doc.body.childNodes)) {
        sanitizeNode(node);
    }
    return doc.body.innerHTML;
}
function sanitizeNode(node) {
    if (node.nodeType === Node.ELEMENT_NODE) {
        const el = node;
        const tag = el.tagName.toLowerCase();
        // Remove dangerous tags entirely (including children)
        if (DANGEROUS_TAGS.has(tag)) {
            el.remove();
            return;
        }
        // Remove disallowed tags but keep children
        if (!ALLOWED_TAGS.has(tag)) {
            while (el.firstChild) {
                el.parentNode?.insertBefore(el.firstChild, el);
            }
            el.remove();
            return;
        }
        // Clean attributes
        for (const attr of Array.from(el.attributes)) {
            if (!ALLOWED_ATTRS.has(attr.name.toLowerCase())) {
                el.removeAttribute(attr.name);
            }
        }
        // Remove style properties that could be dangerous
        const style = el.getAttribute('style') || '';
        const cleanStyle = style.replace(/expression|javascript|vbscript|behavior/gi, '');
        if (cleanStyle !== style) {
            el.setAttribute('style', cleanStyle);
        }
        // Recurse into children
        sanitizeNode(node.firstChild);
    }
    else if (node.nodeType === Node.TEXT_NODE) {
        // Keep text nodes as-is
    }
    else {
        // Remove comment and other node types
        node.parentNode?.removeChild(node);
    }
}
// ─── Initialize State ─────────────────────────────────────────────────
function initState() {
    _state = loadState();
}
};

// ── module: src/editor.ts ──
__mods["src/editor.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Editor = void 0;
// ─── Rich Text Editor Module ──────────────────────────────────────────
const types_1 = require("./types");
const state_1 = require("./state");
class Editor {
    constructor(callbacks) {
        this.currentNoteId = null;
        this.saveTimer = null;
        this.isComposing = false;
        this.contentEl = document.getElementById('editor-content');
        this.callbacks = callbacks;
        this.setupTitleInput();
        this.setupContentEditable();
        this.setupPasteSanitization();
        this.savedIndicator = document.getElementById('saved-indicator');
    }
    setupTitleInput() {
        const headerEl = document.getElementById('editor-header');
        this.titleInput = document.createElement('input');
        this.titleInput.type = 'text';
        this.titleInput.className = 'editor-title-input';
        this.titleInput.placeholder = 'Untitled Note';
        this.titleInput.maxLength = types_1.CONSTANTS.maxTitleLength;
        let titleTimer = null;
        this.titleInput.addEventListener('input', () => {
            if (!this.currentNoteId)
                return;
            const val = this.titleInput.value.trim();
            (0, state_1.updateNote)(this.currentNoteId, { title: val });
            clearTimeout(titleTimer);
            titleTimer = setTimeout(() => this.scheduleSave(), 300);
        });
        headerEl.innerHTML = '';
        headerEl.appendChild(this.titleInput);
    }
    setupContentEditable() {
        this.contentEl.addEventListener('keydown', (e) => this.handleKeyDown(e));
        this.contentEl.addEventListener('input', () => this.onEditorInput());
        this.contentEl.addEventListener('mouseup', () => this.updateToolbarState());
        this.contentEl.addEventListener('keyup', () => this.updateToolbarState());
        // Handle checklist checkbox clicks
        this.contentEl.addEventListener('click', (e) => {
            const cb = e.target.closest('.checklist-checkbox');
            if (cb) {
                e.preventDefault();
                this.toggleChecklistCheckbox(cb);
            }
        });
        // Prevent drag-and-drop of text from messing up content
        this.contentEl.addEventListener('dragover', (e) => e.preventDefault());
        // IME composition events
        this.contentEl.addEventListener('compositionstart', () => { this.isComposing = true; });
        this.contentEl.addEventListener('compositionend', () => {
            this.isComposing = false;
            this.onEditorInput();
        });
    }
    setupPasteSanitization() {
        this.contentEl.addEventListener('paste', (e) => {
            e.preventDefault();
            const clipboardData = e.clipboardData;
            if (!clipboardData)
                return;
            let html = clipboardData.getData('text/html');
            if (!html) {
                const text = clipboardData.getData('text/plain');
                if (text) {
                    document.execCommand('insertText', false, text);
                    this.scheduleSave();
                }
                return;
            }
            html = (0, state_1.sanitizeHTML)(html);
            document.execCommand('insertHTML', false, html);
            this.scheduleSave();
        });
    }
    onEditorInput() {
        if (!this.isComposing) {
            this.updateToolbarState();
            this.scheduleSave();
        }
    }
    // ─── Key Handling ────────────────────────────────────────────────
    handleKeyDown(e) {
        if (this.isComposing)
            return;
        const sel = window.getSelection();
        if (!sel || !sel.rangeCount)
            return;
        // Ctrl/Cmd shortcuts
        if ((e.ctrlKey || e.metaKey) && !e.shiftKey) {
            switch (e.key.toLowerCase()) {
                case 'b':
                    e.preventDefault();
                    this.execFormat('bold');
                    break;
                case 'i':
                    e.preventDefault();
                    this.execFormat('italic');
                    break;
                case 'u':
                    e.preventDefault();
                    this.execFormat('underline');
                    break;
            }
        }
        // Enter key — list continuation and empty item exit
        if (e.key === 'Enter' && !e.ctrlKey && !e.metaKey) {
            const blockEl = this.getCurrentBlockElement();
            if (!blockEl)
                return;
            const li = this.getListItem(blockEl);
            if (li) {
                const textContent = this.getBlockTextContent(li);
                if (textContent.trim() === '' || this.isChecklistEmpty(li)) {
                    e.preventDefault();
                    this.exitList(li);
                    return;
                }
                // Non-empty: let browser continue the list, then clean up structure
                setTimeout(() => this.cleanUpNewListItem(), 0);
            }
        }
        // Space — markdown shortcuts at line start
        if (e.key === ' ') {
            const blockEl = this.getCurrentBlockElement();
            if (!blockEl)
                return;
            const textBeforeCursor = this.getTextBeforeCursor(blockEl);
            this.applyMarkdownShortcut(textBeforeCursor, e);
        }
    }
    getTextBeforeCursor(block) {
        const sel = window.getSelection();
        if (!sel || !sel.rangeCount)
            return '';
        const range = sel.getRangeAt(0);
        const preRange = document.createRange();
        preRange.selectNodeContents(block);
        preRange.setEnd(range.startContainer, range.startOffset);
        return preRange.toString();
    }
    applyMarkdownShortcut(textBefore, e) {
        if (!textBefore)
            return;
        const trimmed = textBefore.trimStart();
        let replacementText = '';
        let tag = null;
        if (trimmed === '# ') {
            replacementText = textBefore.slice(textBefore.length - 2);
            tag = 'h1';
        }
        else if (trimmed === '## ') {
            replacementText = textBefore.slice(textBefore.length - 3);
            tag = 'h2';
        }
        else if (trimmed === '### ') {
            replacementText = textBefore.slice(textBefore.length - 4);
            tag = 'h3';
        }
        else if (trimmed === '- ' || trimmed === '* ') {
            e.preventDefault();
            this.convertToBulletList();
            return;
        }
        else if (trimmed === '[] ') {
            e.preventDefault();
            this.convertToChecklistItem();
            return;
        }
        else if (trimmed === '> ') {
            replacementText = textBefore.slice(textBefore.length - 2);
            tag = 'blockquote';
        }
        if (!tag)
            return;
        const blockEl = this.getCurrentBlockElement();
        if (!blockEl)
            return;
        e.preventDefault();
        // Remove the shortcut prefix and wrap in the target tag
        blockEl.innerHTML = `<${tag}>${replacementText}<br></${tag}>`;
        // Place cursor at end of content
        const sel = window.getSelection();
        if (!sel)
            return;
        const range = document.createRange();
        range.selectNodeContents(blockEl);
        range.collapse(false);
        sel.removeAllRanges();
        sel.addRange(range);
        this.scheduleSave();
    }
    convertToBulletList() {
        const blockEl = this.getCurrentBlockElement();
        if (!blockEl)
            return;
        const textBeforeCursor = this.getTextBeforeCursor(blockEl);
        const contentAfterPrefix = textBeforeCursor.slice(textBeforeCursor.length - 2);
        const ul = document.createElement('ul');
        const li = document.createElement('li');
        li.setAttribute('data-list', 'bullet');
        const span = document.createElement('span');
        span.textContent = contentAfterPrefix;
        li.appendChild(span);
        ul.appendChild(li);
        blockEl.replaceWith(ul);
        const sel = window.getSelection();
        if (!sel)
            return;
        const range = document.createRange();
        range.selectNodeContents(span);
        range.collapse(false);
        sel.removeAllRanges();
        sel.addRange(range);
        this.scheduleSave();
    }
    convertToChecklistItem() {
        const blockEl = this.getCurrentBlockElement();
        if (!blockEl)
            return;
        const textBeforeCursor = this.getTextBeforeCursor(blockEl);
        const contentAfterPrefix = textBeforeCursor.slice(textBeforeCursor.length - 3);
        const ul = document.createElement('ul');
        const li = document.createElement('li');
        li.setAttribute('data-list', 'checklist');
        li.className = 'checklist-item';
        const checkbox = document.createElement('span');
        checkbox.className = 'checklist-checkbox';
        checkbox.setAttribute('contenteditable', 'false');
        li.appendChild(checkbox);
        const textSpan = document.createElement('span');
        textSpan.className = 'checklist-text';
        textSpan.textContent = contentAfterPrefix;
        li.appendChild(textSpan);
        ul.appendChild(li);
        blockEl.replaceWith(ul);
        const sel = window.getSelection();
        if (!sel)
            return;
        const range = document.createRange();
        range.selectNodeContents(textSpan);
        range.collapse(false);
        sel.removeAllRanges();
        sel.addRange(range);
        this.scheduleSave();
    }
    // ─── Block Helpers ────────────────────────────────────────────────
    getCurrentBlockElement() {
        const sel = window.getSelection();
        if (!sel || !sel.rangeCount)
            return null;
        let node = sel.anchorNode;
        while (node && node !== this.contentEl) {
            if (node.nodeType === Node.ELEMENT_NODE) {
                const el = node;
                const display = window.getComputedStyle(el).display;
                if (el.parentElement === this.contentEl ||
                    ['block', 'list-item'].includes(display)) {
                    return el;
                }
            }
            node = node.parentNode;
        }
        return null;
    }
    getListItem(block) {
        if (block.tagName === 'LI')
            return block;
        const li = block.closest('li');
        return li || null;
    }
    getBlockTextContent(el) {
        if (el.getAttribute('data-list') === 'checklist') {
            const textSpan = el.querySelector('.checklist-text');
            return textSpan ? textSpan.textContent || '' : '';
        }
        if (el.getAttribute('data-list') === 'bullet') {
            const span = el.querySelector(':scope > span');
            return span ? span.textContent || '' : el.textContent || '';
        }
        return el.textContent || '';
    }
    isChecklistEmpty(li) {
        if (li.getAttribute('data-list') !== 'checklist')
            return false;
        const textSpan = li.querySelector('.checklist-text');
        return !textSpan || textSpan.textContent?.trim() === '';
    }
    exitList(li) {
        const ul = li.parentElement;
        if (!ul)
            return;
        const p = document.createElement('p');
        p.innerHTML = '<br>';
        ul.removeChild(li);
        if (ul.children.length === 0) {
            ul.replaceWith(p);
        }
        else {
            ul.after(p);
        }
        const sel = window.getSelection();
        if (!sel)
            return;
        const range = document.createRange();
        range.selectNodeContents(p);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
        this.scheduleSave();
    }
    cleanUpNewListItem() {
        const sel = window.getSelection();
        if (!sel || !sel.rangeCount)
            return;
        let node = sel.anchorNode;
        while (node && node !== this.contentEl) {
            if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'LI') {
                const li = node;
                const ul = li.parentElement;
                if (!ul)
                    return;
                const prevLi = li.previousElementSibling;
                const listType = prevLi?.getAttribute('data-list') || 'bullet';
                if (listType === 'checklist' && !li.getAttribute('data-list')) {
                    li.setAttribute('data-list', 'checklist');
                    li.className = 'checklist-item';
                    const checkbox = document.createElement('span');
                    checkbox.className = 'checklist-checkbox';
                    checkbox.setAttribute('contenteditable', 'false');
                    const textSpan = document.createElement('span');
                    textSpan.className = 'checklist-text';
                    while (li.firstChild) {
                        textSpan.appendChild(li.firstChild);
                    }
                    li.insertBefore(checkbox, li.firstChild);
                    li.appendChild(textSpan);
                    this.scheduleSave();
                }
                else if (listType === 'bullet' && !li.getAttribute('data-list')) {
                    li.setAttribute('data-list', 'bullet');
                    const span = document.createElement('span');
                    while (li.firstChild) {
                        span.appendChild(li.firstChild);
                    }
                    li.appendChild(span);
                    this.scheduleSave();
                }
                return;
            }
            node = node.parentNode;
        }
    }
    toggleChecklistCheckbox(checkbox) {
        const isChecked = checkbox.classList.toggle('checked');
        const li = checkbox.parentElement;
        if (li) {
            li.setAttribute('data-checked', String(isChecked));
            li.classList.toggle('checked', isChecked);
        }
        this.scheduleSave();
    }
    // ─── Formatting Commands ──────────────────────────────────────────
    execFormat(command) {
        document.execCommand(command, false, undefined);
        this.contentEl.focus();
        this.updateToolbarState();
        this.scheduleSave();
    }
    applyHeading(level) {
        const block = this.getCurrentBlockElement();
        if (!block)
            return;
        const tag = `h${level}`;
        if (block.tagName.toLowerCase() === tag) {
            this.unwrapToParagraph(block);
            return;
        }
        const newBlock = document.createElement(tag);
        while (block.firstChild) {
            newBlock.appendChild(block.firstChild);
        }
        block.replaceWith(newBlock);
        this.contentEl.focus();
        this.updateToolbarState();
        this.scheduleSave();
    }
    unwrapToParagraph(block) {
        const p = document.createElement('p');
        while (block.firstChild) {
            p.appendChild(block.firstChild);
        }
        block.replaceWith(p);
    }
    applyInlineCode() {
        const sel = window.getSelection();
        if (!sel || !sel.rangeCount || sel.isCollapsed)
            return;
        const range = sel.getRangeAt(0);
        const codeEl = document.createElement('code');
        codeEl.textContent = range.toString();
        range.deleteContents();
        range.insertNode(codeEl);
        range.setStartAfter(codeEl);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
        this.updateToolbarState();
        this.scheduleSave();
    }
    applyBlockquote() {
        const block = this.getCurrentBlockElement();
        if (!block)
            return;
        if (block.tagName.toLowerCase() === 'blockquote') {
            this.unwrapToParagraph(block);
            return;
        }
        const bq = document.createElement('blockquote');
        while (block.firstChild) {
            bq.appendChild(block.firstChild);
        }
        block.replaceWith(bq);
        this.contentEl.focus();
        this.updateToolbarState();
        this.scheduleSave();
    }
    applyHorizontalRule() {
        const block = this.getCurrentBlockElement();
        if (!block)
            return;
        const hr = document.createElement('hr');
        const p = document.createElement('p');
        p.innerHTML = '<br>';
        block.replaceWith(hr, p);
        const sel = window.getSelection();
        if (!sel)
            return;
        const range = document.createRange();
        range.selectNodeContents(p);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
        this.contentEl.focus();
        this.scheduleSave();
    }
    // ─── Toolbar State ────────────────────────────────────────────────
    updateToolbarState() {
        const toolbar = document.getElementById('toolbar');
        if (!toolbar)
            return;
        const commands = {
            bold: 'b', italic: 'i', underline: 'u', strikethrough: 's',
        };
        for (const [cmd, cls] of Object.entries(commands)) {
            const btn = toolbar.querySelector(`[data-cmd="${cls}"]`);
            if (btn) {
                try {
                    const isActive = document.queryCommandState(cmd);
                    btn.classList.toggle('active', isActive);
                }
                catch { /* ignore */ }
            }
        }
        const block = this.getCurrentBlockElement();
        if (block) {
            const tag = block.tagName.toLowerCase();
            toolbar.querySelectorAll('[data-heading]').forEach(btn => {
                btn.classList.toggle('active', btn.getAttribute('data-heading') === tag);
            });
            const bqBtn = toolbar.querySelector('[data-cmd="blockquote"]');
            if (bqBtn)
                bqBtn.classList.toggle('active', tag === 'blockquote');
        }
        // Check for inline code at cursor
        const sel = window.getSelection();
        if (sel && !sel.isCollapsed) {
            let node = sel.anchorNode;
            while (node) {
                if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'CODE') {
                    const codeBtn = toolbar.querySelector('[data-cmd="code"]');
                    if (codeBtn)
                        codeBtn.classList.add('active');
                    break;
                }
                node = node.parentNode;
            }
        }
    }
    // ─── Save / Load ──────────────────────────────────────────────────
    scheduleSave() {
        if (!this.currentNoteId)
            return;
        clearTimeout(this.saveTimer);
        this.saveTimer = setTimeout(() => this.doSave(), types_1.CONSTANTS.autosaveDebounceMs);
    }
    doSave() {
        if (!this.currentNoteId)
            return;
        const html = (0, state_1.sanitizeHTML)(this.contentEl.innerHTML);
        (0, state_1.updateNote)(this.currentNoteId, { content: html });
        this.callbacks.onSave();
        this.showSavedIndicator();
    }
    forceSave() {
        clearTimeout(this.saveTimer);
        if (this.currentNoteId) {
            const html = (0, state_1.sanitizeHTML)(this.contentEl.innerHTML);
            (0, state_1.updateNote)(this.currentNoteId, { content: html });
        }
    }
    showSavedIndicator() {
        this.savedIndicator.textContent = '✓ Saved';
        this.savedIndicator.classList.add('visible');
        setTimeout(() => this.savedIndicator.classList.remove('visible'), 2000);
    }
    loadNote(note) {
        this.currentNoteId = note.id;
        this.titleInput.value = note.title || '';
        this.contentEl.innerHTML = note.content || '<p><br></p>';
        this.restoreChecklistStates();
        if (!note.content || note.content === '<p><br></p>') {
            setTimeout(() => this.contentEl.focus(), 50);
        }
        this.updateToolbarState();
    }
    restoreChecklistStates() {
        const items = this.contentEl.querySelectorAll('li[data-list="checklist"]');
        for (const li of Array.from(items)) {
            const checkedAttr = li.getAttribute('data-checked');
            if (checkedAttr === 'true') {
                const checkbox = li.querySelector('.checklist-checkbox');
                if (checkbox)
                    checkbox.classList.add('checked');
                li.classList.add('checked');
            }
        }
    }
    clear() {
        this.currentNoteId = null;
        this.titleInput.value = '';
        this.contentEl.innerHTML = '';
    }
}
exports.Editor = Editor;
};

// ── module: src/ui.ts ──
__mods["src/ui.ts"] = function (exports, require, module) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppUI = void 0;
// ─── UI Module — Sidebar, Note List, Toolbar ──────────────────────────
const types_1 = require("./types");
const State = __importStar(require("./state"));
class AppUI {
    constructor() {
        this.sidebarPane = document.getElementById('sidebar-pane');
        this.noteListPane = document.getElementById('note-list-pane');
        this.editorPane = document.getElementById('editor-pane');
        this.toolbarEl = document.getElementById('toolbar');
        this.buildToolbar();
        this.setupResizeHandles();
    }
    // ─── Toolbar ──────────────────────────────────────────────────────
    buildToolbar() {
        const buttons = [
            { cmd: 'b', icon: '<b>B</b>', title: 'Bold (Ctrl+B)' },
            { cmd: 'i', icon: '<i>I</i>', title: 'Italic (Ctrl+I)' },
            { cmd: 'u', icon: '<u>U</u>', title: 'Underline (Ctrl+U)' },
            { cmd: 's', icon: '<s>S</s>', title: 'Strikethrough' },
            { cmd: 'code', icon: '&lt;/&gt;', title: 'Inline Code' },
        ];
        let html = '';
        for (const btn of buttons) {
            html += `<button class="toolbar-btn" data-cmd="${btn.cmd}" title="${btn.title}">${btn.icon}</button>`;
        }
        html += '<div class="toolbar-separator"></div>';
        // Headings
        for (let i = 1; i <= 3; i++) {
            html += `<button class="toolbar-btn" data-heading="h${i}" title="Heading ${i}">H${i}</button>`;
        }
        html += '<div class="toolbar-separator"></div>';
        // Block elements
        html += `<button class="toolbar-btn" data-cmd="blockquote" title="Blockquote (>)">❝</button>`;
        html += `<button class="toolbar-btn" data-cmd="hr" title="Horizontal Rule">—</button>`;
        this.toolbarEl.innerHTML = html;
        // Wire up toolbar buttons
        this.toolbarEl.addEventListener('click', (e) => {
            const btn = e.target.closest('.toolbar-btn');
            if (!btn)
                return;
            const cmd = btn.getAttribute('data-cmd');
            const heading = btn.getAttribute('data-heading');
            // Dispatch custom event for the editor to handle
            this.editorPane.dispatchEvent(new CustomEvent('toolbar-action', {
                detail: { cmd, heading },
                bubbles: true,
            }));
        });
    }
    // ─── Sidebar Rendering ────────────────────────────────────────────
    renderSidebar() {
        const s = State.getState();
        const notebooks = s.notebooks;
        const selectedNb = s.selectedNotebookId;
        let html = `
      <div class="sidebar-header">
        <span class="sidebar-title">Notes</span>
        <div class="sidebar-actions">
          <button class="sidebar-btn" id="theme-toggle" title="Toggle theme">${s.darkTheme ? '☀️' : '🌙'}</button>
        </div>
      </div>
      <div class="sidebar-search">
        <input type="text" id="search-input" placeholder="Search notes…" value="${escapeAttr(s.searchQuery)}" />
      </div>
      <nav class="sidebar-nav">
    `;
        // All Notes
        const allCount = State.getNoteCountForNotebook('all');
        html += this.renderNavItem('📋', 'All Notes', 'all', selectedNb === 'all', allCount);
        // Notebooks
        for (const nb of notebooks) {
            const count = State.getNoteCountForNotebook(nb.id);
            const isActive = selectedNb === nb.id;
            html += `
        <div class="notebook-item" data-notebook-id="${nb.id}">
          <div class="notebook-header ${isActive ? 'active' : ''} ${nb.collapsed ? 'collapsed' : ''}" 
               data-action="select-notebook" data-id="${nb.id}">
            <span class="notebook-chevron">${nb.collapsed ? '▶' : '▼'}</span>
            <span class="nav-icon">📁</span>
            <span class="nav-label">${escapeHtml(nb.name)}</span>
            <span class="nav-count">${count}</span>
          </div>
          <div class="notebook-notes ${nb.collapsed ? 'collapsed' : ''}">
      `;
            // Notes in this notebook (only show when not viewing "all")
            if (!nb.collapsed && selectedNb !== 'trash') {
                const notes = State.getFilteredNotes().filter(n => n.notebookId === nb.id);
                for (const note of notes.slice(0, 10)) { // limit preview
                    html += this.renderNoteItem(note, false);
                }
            }
            html += `</div></div>`;
        }
        // Trash
        const trashCount = State.getNoteCountForNotebook('trash');
        html += this.renderNavItem('🗑️', 'Trash', 'trash', selectedNb === 'trash', trashCount);
        html += '</nav>';
        // Footer buttons
        html += `
      <div class="sidebar-footer">
        <button class="add-note-btn" id="new-note-btn">+ New Note</button>
        <button class="add-notebook-btn" id="new-notebook-btn">+ New Notebook</button>
      </div>
    `;
        this.sidebarPane.innerHTML = html;
        this.bindSidebarEvents();
    }
    renderNavItem(icon, label, id, active, count) {
        return `<div class="nav-item ${active ? 'active' : ''}" data-action="select-notebook" data-id="${id}">
      <span class="nav-icon">${icon}</span>
      <span class="nav-label">${escapeHtml(label)}</span>
      <span class="nav-count">${count}</span>
    </div>`;
    }
    renderNoteItem(note, showActions) {
        const snippet = State.extractSnippet(note.content, types_1.CONSTANTS.snippetLength);
        const timeStr = formatTime(note.updatedAt);
        const isActive = State.getState().selectedNoteId === note.id;
        let tagsHtml = '';
        if (note.tags.length > 0) {
            tagsHtml = '<div class="note-item-tags">';
            for (const tag of note.tags.slice(0, 3)) {
                tagsHtml += `<span class="note-tag" data-action="filter-tag" data-tag="${escapeAttr(tag)}">#${escapeHtml(tag)}</span>`;
            }
            if (note.tags.length > 3)
                tagsHtml += `<span class="note-tag">+${note.tags.length - 3}</span>`;
            tagsHtml += '</div>';
        }
        return `
      <div class="note-item ${isActive ? 'active' : ''}" data-action="select-note" data-id="${note.id}">
        <div class="note-item-title">${escapeHtml(note.title || 'Untitled')}</div>
        <div class="note-item-snippet">${escapeHtml(snippet)}</div>
        <div class="note-item-meta">
          ${note.pinned ? '<span class="pin-indicator">📌</span>' : ''}
          <span>${timeStr}</span>
        </div>
        ${tagsHtml}
      </div>`;
    }
    bindSidebarEvents() {
        // Search input
        const searchInput = document.getElementById('search-input');
        if (searchInput) {
            let timer;
            searchInput.addEventListener('input', () => {
                clearTimeout(timer);
                timer = setTimeout(() => State.setSearchQuery(searchInput.value), 200);
            });
        }
        // Theme toggle
        const themeBtn = document.getElementById('theme-toggle');
        if (themeBtn) {
            themeBtn.addEventListener('click', () => State.toggleDarkTheme());
        }
        // New note button
        const newNoteBtn = document.getElementById('new-note-btn');
        if (newNoteBtn) {
            newNoteBtn.addEventListener('click', () => {
                const s = State.getState();
                let targetNb;
                if (s.selectedNotebookId === 'trash') {
                    // Create in first available notebook
                    targetNb = s.notebooks.length > 0 ? s.notebooks[0].id : undefined;
                }
                else if (s.selectedNotebookId !== 'all') {
                    targetNb = s.selectedNotebookId;
                }
                State.createNote(targetNb);
            });
        }
        // New notebook button
        const newNtBtn = document.getElementById('new-notebook-btn');
        if (newNtBtn) {
            newNtBtn.addEventListener('click', () => {
                const name = prompt('Notebook name:');
                if (name?.trim())
                    State.createNotebook(name.trim());
            });
        }
        // Delegate clicks in sidebar nav
        this.sidebarPane.addEventListener('click', (e) => {
            const target = e.target;
            // Tag filter click
            const tagEl = target.closest('[data-action="filter-tag"]');
            if (tagEl) {
                const tag = tagEl.getAttribute('data-tag');
                State.setTagFilter(tag);
                return;
            }
            // Notebook / view selection
            const navItem = target.closest('[data-action="select-notebook"]');
            if (navItem) {
                const id = navItem.getAttribute('data-id');
                State.selectNotebook(id);
                return;
            }
            // Note selection from sidebar preview
            const noteItem = target.closest('[data-action="select-note"]');
            if (noteItem) {
                const id = noteItem.getAttribute('data-id');
                State.selectNote(id);
                return;
            }
        });
        // Notebook header click — toggle collapse or select
        this.sidebarPane.addEventListener('click', (e) => {
            const nbHeader = e.target.closest('.notebook-header');
            if (!nbHeader)
                return;
            e.stopPropagation();
            const id = nbHeader.getAttribute('data-id');
            // Check if clicking the chevron area — toggle collapse
            const rect = nbHeader.getBoundingClientRect();
            const x = e.clientX - rect.left;
            if (x < 24) {
                State.toggleNotebookCollapse(id);
            }
            else {
                State.selectNotebook(id);
            }
        });
        // Right-click on notebooks for context menu
        this.sidebarPane.addEventListener('contextmenu', (e) => {
            const nbHeader = e.target.closest('.notebook-header');
            if (!nbHeader)
                return;
            e.preventDefault();
            const id = nbHeader.getAttribute('data-id');
            showNotebookContextMenu(e.clientX, e.clientY, id);
        });
    }
    // ─── Note List Rendering ──────────────────────────────────────────
    renderNoteList() {
        const s = State.getState();
        const notes = State.getFilteredNotes();
        const viewTitle = getViewTitle(s.selectedNotebookId, s.notebooks);
        let html = `
      <div class="notelist-header">
        <div class="notelist-title-row">
          <span class="notelist-title">${escapeHtml(viewTitle)}</span>
          <div class="notelist-actions">
            <button class="sort-btn" id="sort-toggle" title="Sort order">${getSortLabel(s.sortMode)}</button>
          </div>
        </div>
      </div>
    `;
        // Tag filter bar if active
        if (s.tagFilter) {
            html += `<div class="tag-filter-bar">
        <span class="tag-chip active" data-action="clear-tag-filter">#${escapeHtml(s.tagFilter)}</span>
      </div>`;
        }
        html += '<div class="note-list-container">';
        if (notes.length === 0) {
            const isTrash = s.selectedNotebookId === 'trash';
            html += `
        <div class="empty-state">
          <div class="empty-state-icon">${isTrash ? '🗑️' : '📝'}</div>
          <div class="empty-state-text">${isTrash ? 'Trash is empty' : s.searchQuery ? 'No notes match your search' : 'No notes yet. Create one!'}</div>
        </div>`;
        }
        else {
            for (const note of notes) {
                html += this.renderNoteItem(note, true);
            }
        }
        html += '</div>';
        this.noteListPane.innerHTML = html;
        this.bindNoteListEvents();
    }
    bindNoteListEvents() {
        // Sort toggle
        const sortBtn = document.getElementById('sort-toggle');
        if (sortBtn) {
            sortBtn.addEventListener('click', () => {
                const s = State.getState();
                const modes = ['modified', 'created', 'alphabetical'];
                const idx = modes.indexOf(s.sortMode);
                State.setSortMode(modes[(idx + 1) % modes.length]);
            });
        }
        // Tag filter clear
        this.noteListPane.addEventListener('click', (e) => {
            const tagChip = e.target.closest('[data-action="clear-tag-filter"]');
            if (tagChip) {
                State.setTagFilter(null);
                return;
            }
            // Tag filter from note list
            const tagEl = e.target.closest('[data-action="filter-tag"]');
            if (tagEl) {
                const tag = tagEl.getAttribute('data-tag');
                State.setTagFilter(tag);
                return;
            }
            // Note selection
            const noteItem = e.target.closest('[data-action="select-note"]');
            if (noteItem) {
                const id = noteItem.getAttribute('data-id');
                State.selectNote(id);
                return;
            }
        });
        // Right-click on notes for context menu
        this.noteListPane.addEventListener('contextmenu', (e) => {
            const noteItem = e.target.closest('[data-action="select-note"]');
            if (!noteItem)
                return;
            e.preventDefault();
            const id = noteItem.getAttribute('data-id');
            showNoteContextMenu(e.clientX, e.clientY, id);
        });
        // Drag start for notes (move between notebooks)
        this.noteListPane.addEventListener('dragstart', (e) => {
            const noteItem = e.target.closest('[data-action="select-note"]');
            if (!noteItem)
                return;
            e.dataTransfer.setData('text/plain', noteItem.getAttribute('data-id'));
            e.dataTransfer.effectAllowed = 'move';
        });
        // Make note items draggable
        this.noteListPane.querySelectorAll('.note-item').forEach(el => {
            el.setAttribute('draggable', 'true');
        });
    }
    // ─── Editor Empty State ───────────────────────────────────────────
    showEditorEmptyState() {
        const headerEl = document.getElementById('editor-header');
        const contentEl = document.getElementById('editor-content');
        headerEl.innerHTML = '';
        contentEl.innerHTML = `
      <div class="editor-empty-state">
        <div class="editor-empty-icon">📝</div>
        <div class="editor-empty-text">Select or create a note to get started</div>
      </div>`;
    }
    // ─── Resize Handles ───────────────────────────────────────────────
    setupResizeHandles() {
        this.setupResizeHandle('sidebar-resize-handle', 'sidebar-pane', (width) => {
            State.setPaneLayout({ sidebarWidth: width });
        }, types_1.CONSTANTS.sidebarMinWidth, types_1.CONSTANTS.sidebarMaxWidth);
        this.setupResizeHandle('notelist-resize-handle', 'note-list-pane', (width) => {
            State.setPaneLayout({ noteListWidth: width });
        }, types_1.CONSTANTS.noteListMinWidth, types_1.CONSTANTS.noteListMaxWidth);
    }
    setupResizeHandle(handleId, paneId, onResize, minW, maxW) {
        const handle = document.getElementById(handleId);
        const pane = document.getElementById(paneId);
        let startX = 0;
        let startWidth = 0;
        const onMouseDown = (e) => {
            e.preventDefault();
            startX = e.clientX;
            startWidth = pane.offsetWidth;
            handle.classList.add('active');
            const onMouseMove = (e) => {
                const diff = e.clientX - startX;
                let newWidth = startWidth + diff;
                newWidth = Math.max(minW, Math.min(maxW, newWidth));
                pane.style.width = newWidth + 'px';
                onResize(newWidth);
            };
            const onMouseUp = () => {
                handle.classList.remove('active');
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseUp);
            };
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        };
        handle.addEventListener('mousedown', onMouseDown);
    }
    // ─── Apply Layout State ───────────────────────────────────────────
    applyLayout() {
        const s = State.getState();
        const pl = s.paneLayout;
        this.sidebarPane.style.width = pl.sidebarWidth + 'px';
        this.noteListPane.style.width = pl.noteListWidth + 'px';
        if (pl.sidebarCollapsed) {
            this.sidebarPane.classList.add('collapsed');
        }
        else {
            this.sidebarPane.classList.remove('collapsed');
        }
        // Theme
        document.documentElement.setAttribute('data-theme', s.darkTheme ? 'dark' : 'light');
    }
}
exports.AppUI = AppUI;
// ─── Context Menus ────────────────────────────────────────────────────
function showNoteContextMenu(x, y, noteId) {
    removeContextMenus();
    const menu = document.createElement('div');
    menu.className = 'context-menu';
    menu.style.left = x + 'px';
    menu.style.top = y + 'px';
    const s = State.getState();
    const isTrash = s.selectedNotebookId === 'trash';
    let html = '';
    if (isTrash) {
        html += `<div class="context-menu-item" data-action="restore-note" data-id="${noteId}">↩️ Restore</div>`;
        html += '<div class="context-menu-divider"></div>';
        html += `<div class="context-menu-item danger" data-action="permanent-delete" data-id="${noteId}">🗑️ Delete Permanently</div>`;
    }
    else {
        html += `<div class="context-menu-item" data-action="toggle-pin" data-id="${noteId}">${s.notes.find(n => n.id === noteId)?.pinned ? '📌 Unpin' : '📌 Pin to Top'}</div>`;
        html += `<div class="context-menu-item" data-action="duplicate-note" data-id="${noteId}">📋 Duplicate</div>`;
        html += '<div class="context-menu-divider"></div>';
        // Move to notebook options
        for (const nb of s.notebooks) {
            if (nb.id !== 'trash') {
                html += `<div class="context-menu-item" data-action="move-to-notebook" data-id="${noteId}" data-target="${nb.id}">📁 → ${escapeHtml(nb.name)}</div>`;
            }
        }
        html += '<div class="context-menu-divider"></div>';
        html += `<div class="context-menu-item danger" data-action="delete-note" data-id="${noteId}">🗑️ Move to Trash</div>`;
    }
    menu.innerHTML = html;
    document.body.appendChild(menu);
    // Adjust position if off-screen
    const rect = menu.getBoundingClientRect();
    if (rect.right > window.innerWidth)
        menu.style.left = (x - rect.width) + 'px';
    if (rect.bottom > window.innerHeight)
        menu.style.top = (y - rect.height) + 'px';
    menu.addEventListener('click', (e) => {
        const item = e.target.closest('.context-menu-item');
        if (!item)
            return;
        const action = item.getAttribute('data-action');
        const id = item.getAttribute('data-id');
        switch (action) {
            case 'toggle-pin':
                State.togglePin(id);
                break;
            case 'duplicate-note':
                State.duplicateNote(id);
                break;
            case 'delete-note':
                State.moveToTrash(id);
                break;
            case 'restore-note':
                State.restoreFromTrash(id);
                break;
            case 'permanent-delete':
                State.permanentlyDelete(id);
                break;
            case 'move-to-notebook':
                const target = item.getAttribute('data-target');
                State.moveNoteToNotebook(id, target);
                break;
        }
        removeContextMenus();
    });
    // Close on outside click
    setTimeout(() => {
        document.addEventListener('click', () => removeContextMenus(), { once: true });
    }, 0);
}
function showNotebookContextMenu(x, y, notebookId) {
    removeContextMenus();
    const menu = document.createElement('div');
    menu.className = 'context-menu';
    menu.style.left = x + 'px';
    menu.style.top = y + 'px';
    menu.innerHTML = `
    <div class="context-menu-item" data-action="rename-notebook" data-id="${notebookId}">✏️ Rename</div>
    <div class="context-menu-divider"></div>
    <div class="context-menu-item danger" data-action="delete-notebook" data-id="${notebookId}">🗑️ Delete Notebook</div>
  `;
    document.body.appendChild(menu);
    menu.addEventListener('click', (e) => {
        const item = e.target.closest('.context-menu-item');
        if (!item)
            return;
        const action = item.getAttribute('data-action');
        const id = item.getAttribute('data-id');
        switch (action) {
            case 'rename-notebook': {
                const name = prompt('Rename notebook:');
                if (name?.trim())
                    State.renameNotebook(id, name.trim());
                break;
            }
            case 'delete-notebook': {
                if (confirm('Delete this notebook? Notes will be moved to Trash.')) {
                    State.deleteNotebook(id);
                }
                break;
            }
        }
        removeContextMenus();
    });
    setTimeout(() => {
        document.addEventListener('click', () => removeContextMenus(), { once: true });
    }, 0);
}
function removeContextMenus() {
    document.querySelectorAll('.context-menu, .move-menu').forEach(el => el.remove());
}
// ─── Utility Functions ────────────────────────────────────────────────
function escapeHtml(str) {
    const div = document.createElement('div');
    div.textContent = str;
    return div.innerHTML;
}
function escapeAttr(str) {
    return str.replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function formatTime(ts) {
    const now = Date.now();
    const diff = now - ts;
    if (diff < 60000)
        return 'Just now';
    if (diff < 3600000)
        return `${Math.floor(diff / 60000)}m ago`;
    if (diff < 86400000)
        return `${Math.floor(diff / 3600000)}h ago`;
    const d = new Date(ts);
    const month = d.toLocaleString('default', { month: 'short' });
    return `${month} ${d.getDate()}`;
}
function getViewTitle(viewId, notebooks) {
    if (viewId === 'all')
        return 'All Notes';
    if (viewId === 'trash')
        return 'Trash';
    const nb = notebooks.find(n => n.id === viewId);
    return nb?.name || 'Notes';
}
function getSortLabel(mode) {
    switch (mode) {
        case 'modified': return '↕ Modified';
        case 'created': return '↕ Created';
        case 'alphabetical': return 'A→Z Alpha';
        default: return '↕ Modified';
    }
}
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"qwen3.6-27b-mtp","provider":"lmstudio","persona":"typescript","sessionId":"f54883c3-c792-4f89-b6b1-6217c612cd7f","tokensIn":4641537,"tokensOut":53493,"tokensTotal":4695030,"turns":85,"toolCalls":84,"failedToolCalls":6,"timestamp":"2026-07-31T00:46:46.745Z"} -->