← Notes results

Notes

nex agi Nex N2.5 mini GGUF · typescript

Initial view of Notes

Browser check: Artifact runtime error. This is a technical check, not a quality review.

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 78,024 bytes · SHA-256 6dcad44ebd48
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Notely — Notes</title>
<style>
/* ── css: app ── */
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600&display=swap');
:root {
  --bg: #f4f1ea; --surface: #fffdf8; --surface-2: #f8f5ee; --surface-3: #f0ebe1; --ink: #211d18; --muted: #857d72; --faint: #b8b0a4; --line: #e4ded2; --line-soft: #eee8dd; --accent: #b65a3b; --accent-soft: #f5e3db; --accent-ink: #8d3f28; --green: #58815a; --danger: #b84f4f; --shadow: 0 18px 55px rgba(66, 51, 30, .09); --radius: 14px; --sans: 'DM Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; --serif: 'Source Serif 4', Georgia, serif;
}
:root[data-theme='dark'] {
  --bg: #171816; --surface: #1f211e; --surface-2: #252823; --surface-3: #2e322b; --ink: #ece7dc; --muted: #9a9b90; --faint: #62655b; --line: #383c34; --line-soft: #2a2d27; --accent: #d07c58; --accent-soft: #3c2b24; --accent-ink: #f1a584; --green: #97b989; --danger: #d47a76; --shadow: 0 18px 55px rgba(0,0,0,.3);
}
* { box-sizing: border-box; }
html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; background: var(--bg); color: var(--ink); font-family: var(--sans); }
button, input { font: inherit; }
button { color: inherit; }
.app-layout { display: flex; width: 100%; height: 100vh; min-height: 0; background: var(--bg); }
.sidebar { flex: 0 0 var(--sidebar-width); width: var(--sidebar-width); min-width: 0; height: 100%; overflow: hidden; display: flex; flex-direction: column; background: var(--surface); border-right: 1px solid var(--line); transition: flex-basis .22s ease, width .22s ease; }
.sidebar.collapsed { flex-basis: 0 !important; width: 0 !important; border-right: 0; }
.sidebar-resize, .list-resize { flex: 0 0 5px; cursor: col-resize; z-index: 10; }
.sidebar-resize { margin-left: -3px; }
.list-pane { flex: 0 0 var(--list-width); width: var(--list-width); min-width: 0; height: 100%; display: flex; flex-direction: column; background: var(--surface-2); border-right: 1px solid var(--line); overflow: hidden; }
.list-resize { height: 100%; }
.editor-pane { flex: 1 1 auto; min-width: 0; height: 100%; overflow: hidden; display: flex; flex-direction: column; background: var(--bg); }
.icon-btn { width: 32px; height: 32px; border: 0; border-radius: 9px; display: inline-grid; place-items: center; background: transparent; color: var(--muted); cursor: pointer; transition: .16s ease; }
.icon-btn:hover { background: var(--surface-3); color: var(--ink); }
.icon-btn:active { transform: scale(.96); }
.icon-btn.tiny { width: 24px; height: 24px; border-radius: 7px; }
.icon-btn svg, .nav-item svg, .section-label svg, .notebook-main svg, .empty-icon svg, .primary-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.brand { height: 70px; flex: 0 0 70px; display: flex; align-items: center; gap: 11px; padding: 0 14px 0 18px; border-bottom: 1px solid var(--line-soft); }
.brand-mark { width: 31px; height: 31px; border-radius: 9px; display: grid; place-items: center; color: #fff; background: var(--accent); font-weight: 700; font-size: 15px; letter-spacing: -.5px; box-shadow: 0 5px 13px rgba(182, 90, 59, .25); }
.brand-copy { min-width: 0; display: grid; line-height: 1.1; }
.brand-copy strong { font-size: 14px; letter-spacing: -.1px; }
.brand-copy span { margin-top: 4px; color: var(--muted); font-size: 10px; letter-spacing: .02em; }
.menu-toggle { margin-left: auto; }
.view-nav { padding: 14px 10px 9px; display: grid; gap: 3px; }
.nav-item { width: 100%; height: 36px; display: flex; align-items: center; gap: 9px; padding: 0 10px; border: 0; border-radius: 9px; background: transparent; color: var(--muted); text-align: left; cursor: pointer; font-size: 12.5px; font-weight: 600; transition: .16s ease; }
.nav-item:hover { background: var(--surface-3); color: var(--ink); }
.nav-item.active { background: var(--accent-soft); color: var(--accent-ink); }
.nav-item span:nth-child(2) { flex: 1; }
.count-badge { color: var(--faint); font-size: 11px; font-weight: 500; }
.sidebar-section { min-height: 0; flex: 1; display: flex; flex-direction: column; padding: 5px 10px 14px; overflow-y: auto; }
.section-label { height: 32px; display: flex; align-items: center; justify-content: space-between; padding: 0 2px; color: var(--muted); text-transform: uppercase; font-size: 10px; font-weight: 700; letter-spacing: .09em; }
.notebook-list { display: grid; gap: 3px; }
.notebook-row { position: relative; border-radius: 10px; transition: background .16s ease; }
.notebook-row:hover, .notebook-row.drop-target { background: var(--surface-3); }
.notebook-main { width: 100%; height: 32px; display: flex; align-items: center; gap: 8px; padding: 0 5px 0 7px; border: 0; border-radius: 8px; background: transparent; color: var(--muted); text-align: left; cursor: pointer; font-size: 12.5px; }
.notebook-main:hover, .notebook-row.drop-target .notebook-main { color: var(--ink); background: var(--surface-2); }
.notebook-main > span:nth-child(2) { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notebook-actions { position: absolute; right: 5px; top: 3px; display: none; gap: 1px; }
.notebook-row:hover .notebook-actions { display: flex; }
.notebook-notes { display: grid; gap: 3px; padding: 3px 8px 4px 37px; }
.mini-note { min-height: 24px; display: flex; align-items: center; gap: 7px; padding: 0 4px; border-radius: 6px; overflow: hidden; color: var(--faint); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.mini-note:hover { color: var(--ink); background: var(--surface-3); }
.mini-note.selected { color: var(--accent-ink); background: var(--accent-soft); }
.mini-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); flex: 0 0 5px; }
.sidebar-footer { flex: 0 0 auto; padding: 10px 16px 13px; border-top: 1px solid var(--line-soft); color: var(--muted); }
.storage-pill { display: flex; align-items: center; gap: 7px; font-size: 11px; }
.storage-pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px rgba(88,129,90,.12); }
.shortcut-hint { margin-top: 8px; color: var(--faint); font-size: 10px; }
.list-header { flex: 0 0 auto; padding: 21px 18px 12px; border-bottom: 1px solid var(--line); }
.list-header-main { display: flex; align-items: center; gap: 10px; }
.eyebrow { margin-bottom: 5px; color: var(--accent-ink); font-size: 9.5px; font-weight: 700; letter-spacing: .11em; }
.list-header h1 { margin: 0; font-size: 22px; letter-spacing: -.035em; font-weight: 650; }
.list-toolbar { display: flex; align-items: center; gap: 8px; margin-top: 17px; }
.search-box { position: relative; min-width: 0; flex: 1; display: flex; align-items: center; height: 34px; padding: 0 6px 0 10px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface); transition: .16s ease; }
.search-box:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.search-box > svg { width: 15px; height: 15px; color: var(--faint); fill: none; stroke: currentColor; stroke-width: 1.8; }
.search-box input { width: 100%; min-width: 0; height: 100%; border: 0; outline: 0; background: transparent; color: var(--ink); font-size: 12px; }
.search-box input::placeholder { color: var(--faint); }
.search-clear { width: 22px; height: 22px; border: 0; border-radius: 6px; display: grid; place-items: center; background: transparent; color: var(--muted); cursor: pointer; }
.search-clear:hover { background: var(--surface-3); color: var(--ink); }
.search-clear svg { width: 13px; height: 13px; }
.primary-btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; height: 34px; padding: 0 12px; border: 0; border-radius: 9px; background: var(--accent); color: #fff; cursor: pointer; font-size: 12px; font-weight: 650; box-shadow: 0 6px 15px rgba(182,90,59,.2); transition: .16s ease; }
.primary-btn:hover { background: var(--accent-ink); transform: translateY(-1px); box-shadow: 0 8px 18px rgba(182,90,59,.27); }
.primary-btn:active { transform: translateY(0); }
.primary-btn svg { width: 14px; height: 14px; }
.list-meta { display: flex; align-items: center; gap: 8px; margin-top: 12px; color: var(--muted); font-size: 11px; }
.tag-chip { padding: 4px 7px; border-radius: 6px; background: var(--accent-soft); color: var(--accent-ink); font-size: 10px; font-weight: 650; }
.note-list { flex: 1; overflow-y: auto; padding: 9px 9px 20px; }
.note-row { position: relative; margin-bottom: 4px; padding: 13px 12px 11px; border: 1px solid transparent; border-radius: 11px; cursor: pointer; transition: .16s ease; }
.note-row:hover { background: var(--surface); border-color: var(--line-soft); }
.note-row.selected { background: var(--accent-soft); border-color: rgba(182,90,59,.22); box-shadow: 0 4px 12px rgba(182,90,59,.07); }
.note-row.dragging { opacity: .42; }
.note-row-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.note-title { min-width: 0; overflow: hidden; color: var(--ink); text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; font-weight: 650; letter-spacing: -.01em; }
.note-row:not(.selected) .note-title { color: var(--ink); }
.note-row time { flex: 0 0 auto; color: var(--faint); font-size: 10.5px; }
.note-snippet { position: relative; margin-top: 6px; overflow: hidden; color: var(--muted); text-overflow: ellipsis; white-space: nowrap; font-size: 11.5px; line-height: 1.45; }
.note-snippet mark { padding: 0 1px; border-radius: 3px; background: rgba(208,124,88,.2); color: var(--accent-ink); }
.note-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px; }
.mini-tag { height: 20px; padding: 0 7px; border: 1px solid var(--line); border-radius: 20px; background: transparent; color: var(--muted); cursor: pointer; font-size: 10px; transition: .15s ease; }
.mini-tag:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }
.empty-list { min-height: 45%; display: grid; justify-items: center; align-content: center; gap: 6px; color: var(--muted); text-align: center; }
.empty-icon { width: 39px; height: 39px; display: grid; place-items: center; border-radius: 12px; background: var(--surface-3); color: var(--faint); }
.empty-list strong { color: var(--ink); font-size: 13px; }
.empty-list span { font-size: 11px; }
.editor-pane { position: relative; }
.mobile-brand { display: none; }
.editor-topbar { flex: 0 0 55px; display: flex; align-items: center; padding: 0 23px; border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 94%, transparent); }
.editor-path { color: var(--muted); font-size: 12px; }
.editor-path span { margin: 0 7px; color: var(--faint); }
.editor-scroll { min-height: 0; flex: 1; overflow-y: auto; }
.editor-canvas { width: min(720px, calc(100% - 96px)); margin: 0 auto; padding: 48px 0 100px; }
.title-input-wrap { border-bottom: 1px solid var(--line-soft); }
.note-title-input { width: 100%; padding: 0 0 13px; border: 0; outline: 0; background: transparent; color: var(--ink); font-family: var(--serif); font-size: clamp(31px, 4vw, 45px); font-weight: 600; letter-spacing: -.045em; line-height: 1.1; }
.note-title-input::placeholder { color: var(--faint); }
.save-indicator { display: flex; align-items: center; justify-content: center; gap: 6px; height: 25px; color: var(--muted); font-size: 10.5px; }
.save-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--green); }
.save-indicator:not(.saved) .save-dot { background: var(--accent); }
.toolbar { min-height: 39px; margin: 10px -5px 0; display: flex; align-items: center; flex-wrap: wrap; gap: 2px; border-radius: 10px; transition: .15s ease; }
.toolbar:hover { background: color-mix(in srgb, var(--surface-3) 65%, transparent); }
.toolbar button { min-width: 31px; height: 29px; padding: 0 7px; border: 0; border-radius: 7px; background: transparent; color: var(--muted); cursor: pointer; font-size: 12px; font-weight: 650; transition: .14s ease; }
.toolbar button:hover { background: var(--surface-2); color: var(--ink); }
.toolbar button.active { background: var(--accent-soft); color: var(--accent-ink); }
.toolbar-divider { width: 1px; height: 18px; margin: 0 5px; background: var(--line); }
.toolbar-bullets { letter-spacing: -1px; font-size: 14px !important; line-height: 1; }
.toolbar-number { color: var(--accent-ink); font-size: 11px !important; }
.tags-bar { min-height: 32px; display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 11px; }
.tag-label { color: var(--faint); font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.tag-input-wrap { height: 27px; min-width: 130px; max-width: 230px; display: flex; align-items: center; gap: 4px; padding: 0 9px; border: 1px solid transparent; border-radius: 8px; color: var(--faint); }
.tag-input-wrap:focus-within { border-color: var(--line); background: var(--surface); }
.tag-input-wrap input { min-width: 0; flex: 1; border: 0; outline: 0; background: transparent; color: var(--ink); font-size: 11px; }
.editor-content { min-height: 430px; outline: 0; color: var(--ink); font-family: var(--serif); font-size: 17.5px; line-height: 1.78; overflow-wrap: anywhere; caret-color: var(--accent); }
.editor-content > :first-child { margin-top: 0; }
.editor-content h1 { margin: 0 0 18px; font-family: var(--serif); font-size: 35px; line-height: 1.2; letter-spacing: -.035em; }
.editor-content h2 { margin: 34px 0 13px; font-family: var(--serif); font-size: 26px; line-height: 1.3; letter-spacing: -.02em; }
.editor-content h3 { margin: 28px 0 10px; font-family: var(--serif); font-size: 20px; line-height: 1.35; }
.editor-content p { margin: 0 0 14px; }
.editor-content ul, .editor-content ol { margin: 8px 0 17px; padding-left: 24px; }
.editor-content li { margin: 5px 0; padding-left: 3px; }
.editor-content blockquote { margin: 22px 0; padding: 2px 0 2px 22px; border-left: 3px solid var(--accent); color: color-mix(in srgb, var(--ink) 78%, var(--muted)); font-size: 19px; line-height: 1.6; }
.editor-content code { padding: 2px 5px; border-radius: 5px; background: var(--surface-3); color: var(--accent-ink); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .83em; }
.editor-content hr { margin: 30px 0; border: 0; border-top: 1px solid var(--line); }
.editor-content input[type='checkbox'] { width: 16px; height: 16px; margin: 3px 8px 0 0; accent-color: var(--accent); vertical-align: middle; cursor: pointer; }
.check-label { display: inline-block; transition: .16s ease; }
.check-label.checked { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--accent); }
.editor-footer { min-height: 31px; display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 38px; padding-top: 12px; border-top: 1px solid var(--line-soft); color: var(--faint); font-size: 10.5px; }
.footer-hint { font-style: italic; }
.tag-pill { padding: 4px 8px; border: 1px solid var(--line); border-radius: 20px; background: var(--surface); color: var(--muted); cursor: pointer; font-size: 10.5px; }
.tag-pill:hover { border-color: var(--accent); color: var(--accent-ink); }
.editor-actions { position: absolute; z-index: 5; top: 54px; right: 20px; width: 220px; padding: 7px; border: 1px solid var(--line); border-radius: 11px; background: var(--surface); box-shadow: var(--shadow); display: none; }
.editor-actions.open { display: grid; }
.editor-actions button { width: 100%; height: 36px; display: flex; align-items: center; gap: 10px; padding: 0 10px; border: 0; border-radius: 8px; background: transparent; color: var(--muted); text-align: left; cursor: pointer; font-size: 12px; }
.editor-actions button:hover { background: var(--surface-3); color: var(--ink); }
.editor-actions button[data-action='delete'], .editor-actions button[data-action='hard-delete'] { color: var(--danger); }
.action-divider { height: 1px; margin: 5px; background: var(--line-soft); }
.empty-editor { flex: 1; display: grid; justify-items: center; align-content: center; gap: 11px; text-align: center; }
.empty-mark { width: 52px; height: 52px; display: grid; place-items: center; border-radius: 16px; background: var(--accent-soft); color: var(--accent-ink); font-family: var(--serif); font-size: 25px; font-weight: 600; }
.empty-editor h2 { margin: 4px 0 0; font-family: var(--serif); font-size: 25px; letter-spacing: -.025em; }
.empty-editor p { max-width: 320px; margin: 0 0 10px; color: var(--muted); font-size: 13px; line-height: 1.6; }
::selection { background: rgba(208,124,88,.28); }
@media (max-width: 900px) {
  .editor-canvas { width: calc(100% - 42px); padding-top: 30px; }
  .editor-topbar { padding: 0 14px; }
  .editor-actions { right: 10px; }
}
@media (max-width: 680px) {
  .sidebar { display: none; }
  .list-pane { flex-basis: 190px; width: 190px; }
  .list-header { padding: 15px 12px 10px; }
  .list-header h1 { font-size: 19px; }
  .editor-canvas { width: calc(100% - 30px); padding-top: 26px; }
  .editor-topbar { height: 48px; flex-basis: 48px; }
  .editor-content { font-size: 16px; }
}
</style>
</head>
<body>
<div id="app"></div>
<div id="app">
  <div id="layout" class="app-layout">
    <aside id="sidebar" class="sidebar" aria-label="Notebook sidebar"></aside>
    <div id="sidebar-resize" class="resize-handle sidebar-resize" aria-hidden="true"></div>
    <section id="list-pane" class="list-pane" aria-label="Note list">
      <div id="list-header" class="list-header"></div>
      <div id="note-list" class="note-list"></div>
    </section>
    <div id="list-resize" class="resize-handle list-resize" aria-hidden="true"></div>
    <main id="editor-pane" class="editor-pane" aria-label="Editor"></main>
  </div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./editor":"src/editor.ts","./model":"src/model.ts"},"src/model.ts":{"./types":"src/types.ts"},"src/editor.ts":{"./model":"src/model.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 });
const editor_1 = require("./editor");
const model_1 = require("./model");
const qs = (selector) => document.querySelector(selector);
const icon = (name) => ({ menu: '<svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"/></svg>', plus: '<svg viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg>', search: '<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/></svg>', sun: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.3 11.3 1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>', moon: '<svg viewBox="0 0 24 24"><path d="M20.5 14.2A8.5 8.5 0 0 1 9.8 3.5a8.5 8.5 0 1 0 10.7 10.7Z"/></svg>', trash: '<svg viewBox="0 0 24 24"><path d="M4 7h16M9 7V4h6v3m3 0-1 14H7L6 7m4 3v6m4-6v6"/></svg>', pin: '<svg viewBox="0 0 24 24"><path d="M14 4l6 6-3 1-4 6-2-5-5 3 3-5-5-2 6-4 1-3 3 2Z"/></svg>', copy: '<svg viewBox="0 0 24 24"><rect x="8" y="8" width="11" height="11" rx="2"/><path d="M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3"/></svg>', chev: '<svg viewBox="0 0 24 24"><path d="m9 6 6 6-6 6"/></svg>', back: '<svg viewBox="0 0 24 24"><path d="m15 18-6-6 6-6"/></svg>', folder: '<svg viewBox="0 0 24 24"><path d="M3 6a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6Z"/></svg>', layers: '<svg viewBox="0 0 24 24"><path d="m12 3 9 5-9 5-9-5 9-5Zm9 10-9 5-9-5"/></svg>', archive: '<svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="5" rx="1"/><path d="M5 9v10h14V9M10 13h4"/></svg>', dots: '<svg viewBox="0 0 24 24"><circle cx="5" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/></svg>', x: '<svg viewBox="0 0 24 24"><path d="m6 6 12 12M18 6 6 18"/></svg>', check: '<svg viewBox="0 0 24 24"><path d="m5 12 4 4L19 6"/></svg>', move: '<svg viewBox="0 0 24 24"><path d="M4 7h12m0 0-3-3m3 3-3 3M4 17h12m0 0 3-3m-3 3-3 3"/></svg>' }[name]);
const svgIcon = (name) => icon(name);
const escapeHTML = (value) => value.replace(/[&<>"']/g, (character) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }[character] ?? character));
const safeText = (value) => escapeHTML(value);
const state = { app: (0, model_1.getApplication)(), ui: (0, model_1.getUI)(), editor: null, selectedNote: null, dragNote: null };
const DEFAULT_NOTEBOOK_ID = 'notes-inbox';
const safePromptText = (value) => (value ?? '').trim();
function saveUI() { try {
    localStorage.setItem('notely.ui.v1', JSON.stringify(state.ui));
}
catch { /* storage unavailable */ } }
function persistApp() { try {
    localStorage.setItem('notely.data.v1', JSON.stringify(state.app));
}
catch { /* storage unavailable */ } }
function applyTheme() { document.documentElement.dataset.theme = state.app.theme; const themeButton = qs('#theme-toggle'); if (themeButton) {
    themeButton.innerHTML = state.app.theme === 'dark' ? svgIcon('sun') : svgIcon('moon');
    themeButton.setAttribute('aria-label', state.app.theme === 'dark' ? 'Switch to light theme' : 'Switch to dark theme');
} }
function renderAll() { applyTheme(); renderSidebar(); renderListHeader(); renderList(); renderEditor(); }
function setView(view) { state.ui.activeView = view; if (view !== 'notes')
    state.ui.activeTag = null; if (view === 'notes' && !state.ui.selectedNoteId)
    state.ui.selectedNoteId = state.app.notes.find((note) => !note.deleted)?.id ?? null; if (view === 'all-notes' && !state.ui.selectedNoteId)
    state.ui.selectedNoteId = state.app.notes.find((note) => !note.deleted)?.id ?? null; if (view === 'trash')
    state.ui.selectedNoteId = null; saveUI(); renderAll(); }
function renderSidebar() {
    const sidebar = qs('#sidebar');
    const counts = (notebookId) => state.app.notes.filter((note) => note.notebookId === notebookId && !note.deleted).length;
    const viewItems = [['notes', 'folder', 'Notes', state.app.notes.filter((note) => !note.deleted).length], ['all-notes', 'layers', 'All Notes', state.app.notes.filter((note) => !note.deleted).length], ['trash', 'archive', 'Trash', state.app.notes.filter((note) => note.deleted).length]];
    sidebar.innerHTML = `<div class="brand"><div class="brand-mark">N</div><div class="brand-copy"><strong>Notely</strong><span>Private by design</span></div><button class="icon-btn menu-toggle" id="sidebar-collapse" title="Collapse sidebar">${svgIcon('menu')}</button></div><nav class="view-nav" aria-label="Views">${viewItems.map(([view, viewIcon, label, count]) => `<button class="nav-item ${state.ui.activeView === view ? 'active' : ''}" data-view="${view}">${svgIcon(viewIcon)}<span>${label}</span><span class="count-badge">${count}</span></button>`).join('')}</nav><div class="sidebar-section"><div class="section-label"><span>Notebooks</span><button class="icon-btn tiny" id="add-notebook" title="New notebook">${svgIcon('plus')}</button></div><div class="notebook-list">${state.app.notebooks.map((notebook) => `<div class="notebook-row" data-notebook="${safeText(notebook.id)}"><button class="notebook-main" data-notebook-id="${safeText(notebook.id)}">${svgIcon('folder')}<span class="notebook-name">${safeText(notebook.name)}</span><span class="count-badge">${counts(notebook.id)}</span></button><div class="notebook-actions"><button class="icon-btn tiny notebook-rename" title="Rename notebook">${svgIcon('chev')}</button><button class="icon-btn tiny notebook-delete" title="Delete notebook">${svgIcon('trash')}</button></div><div class="notebook-notes">${state.app.notes.filter((note) => note.notebookId === notebook.id && !note.deleted).slice(0, 5).map((note) => `<div class="mini-note ${state.ui.selectedNoteId === note.id ? 'selected' : ''}" data-mini-note="${safeText(note.id)}"><span class="mini-dot"></span>${safeText(model_1.model.titleFromHTML(note.bodyHTML))}</div>`).join('')}</div></div>`).join('')}</div></div><div class="sidebar-footer"><div class="storage-pill"><span class="dot"></span> Local storage</div><div class="shortcut-hint">⌘K search · ⌘B bold</div></div>`;
    sidebar.classList.toggle('collapsed', state.ui.layout.sidebarCollapsed);
    sidebar.style.setProperty('--sidebar-width', `${state.ui.layout.sidebarWidth}px`);
    qs('#layout').style.setProperty('--sidebar-width', `${state.ui.layout.sidebarWidth}px`);
    qs('#layout').style.setProperty('--list-width', `${state.ui.layout.listWidth}px`);
    sidebar.querySelectorAll('[data-view]').forEach((button) => button.addEventListener('click', () => setView(button.dataset.view)));
    sidebar.querySelectorAll('[data-notebook-id]').forEach((button) => button.addEventListener('click', () => { state.ui.activeView = 'notes'; state.ui.activeTag = null; state.ui.selectedNoteId = state.app.notes.find((note) => note.notebookId === button.dataset.notebookId && !note.deleted)?.id ?? null; saveUI(); renderAll(); }));
    qs('#add-notebook')?.addEventListener('click', () => { const promptName = window.prompt('New notebook name', 'New notebook'); if (promptName && promptName.trim()) {
        const cleanName = promptName.trim();
        try {
            model_1.model.createNotebook(cleanName);
            state.app = (0, model_1.getApplication)();
            state.ui = (0, model_1.getUI)();
            renderAll();
        }
        catch (error) {
            window.alert(error instanceof Error ? error.message : 'Could not create notebook');
        }
    } });
    sidebar.querySelectorAll('[data-notebook]').forEach((row) => { const notebookId = row.dataset.notebook; row.querySelector('.notebook-rename')?.addEventListener('click', () => { const notebook = state.app.notebooks.find((item) => item.id === notebookId); if (!notebook)
        return; const promptName = window.prompt('Rename notebook', notebook.name ?? 'Notes'); const cleanName = safePromptText(promptName); if (cleanName) {
        try {
            model_1.model.renameNotebook(notebookId, cleanName);
            state.app = (0, model_1.getApplication)();
            state.ui = (0, model_1.getUI)();
            renderAll();
        }
        catch (error) {
            window.alert(error instanceof Error ? error.message : 'Could not rename notebook');
        }
    } }); row.querySelector('.notebook-delete')?.addEventListener('click', () => { if (notebookId === DEFAULT_NOTEBOOK_ID) {
        window.alert('The default Notes notebook cannot be deleted');
        return;
    } if (window.confirm('Delete notebook and move its notes to Trash?')) {
        model_1.model.deleteNotebook(notebookId);
        state.app = (0, model_1.getApplication)();
        state.ui = (0, model_1.getUI)();
        setView('all-notes');
    } }); row.addEventListener('dragover', (event) => { event.preventDefault(); row.classList.add('drop-target'); }); row.addEventListener('dragleave', () => row.classList.remove('drop-target')); row.addEventListener('drop', (event) => { event.preventDefault(); row.classList.remove('drop-target'); const noteId = state.dragNote?.id; if (noteId && noteId !== notebookId) {
        model_1.model.moveNote(noteId, notebookId);
        state.app = (0, model_1.getApplication)();
        state.ui = (0, model_1.getUI)();
        state.dragNote = null;
        renderAll();
    } }); });
}
function renderListHeader() {
    const header = qs('#list-header');
    const titleValue = state.ui.activeView === 'notes' ? 'Notes' : state.ui.activeView === 'all-notes' ? 'All Notes' : 'Trash';
    const title = String(titleValue || '');
    const safeTitle = (title || 'Notes').trim();
    const safeQuery = String(state.ui.searchQuery || '');
    const sortLabel = state.ui.sortOrder === 'modified' ? 'Recently modified' : state.ui.sortOrder === 'created' ? 'Newest first' : 'A–Z';
    const tagMarkup = state.ui.activeTag ? `<span class="tag-chip">#${safeText(state.ui.activeTag)}</span>` : '';
    header.innerHTML = `<div class="list-header-main"><button class="icon-btn list-back" id="list-back" title="Back">${svgIcon('back')}</button><div><div class="eyebrow">LIBRARY</div><h1>${safeText(safeTitle)}</h1></div></div><div class="list-toolbar"><div class="search-box">${svgIcon('search')}<input id="global-search" type="search" placeholder="Search notes" value="${safeText(safeQuery)}"><button class="search-clear" id="search-clear" title="Clear search">${svgIcon('x')}</button></div><button class="icon-btn" id="sort-notes" title="Sort order">${svgIcon('chev')}</button><button class="primary-btn" id="new-note">${svgIcon('plus')}<span>New note</span></button></div><div class="list-meta">${tagMarkup}<span>${safeText(sortLabel)}</span></div>`;
    const search = qs('#global-search');
    search?.addEventListener('input', (event) => { state.ui.searchQuery = event.target.value; state.ui.activeTag = null; saveUI(); renderList(); renderListHeader(); });
    qs('#search-clear')?.addEventListener('click', () => { state.ui.searchQuery = ''; state.ui.activeTag = null; saveUI(); renderList(); renderListHeader(); });
    qs('#sort-notes')?.addEventListener('click', () => { state.ui.sortOrder = state.ui.sortOrder === 'modified' ? 'created' : state.ui.sortOrder === 'created' ? 'alphabetical' : 'modified'; saveUI(); renderListHeader(); renderList(); });
    qs('#new-note')?.addEventListener('click', () => { model_1.model.createNote(); state.app = (0, model_1.getApplication)(); state.ui = (0, model_1.getUI)(); state.selectedNote = model_1.model.getNote(state.ui.selectedNoteId) ?? null; renderAll(); });
    qs('#list-back')?.addEventListener('click', () => { if (state.ui.activeView !== 'notes')
        setView('notes'); });
}
function renderList() {
    const list = qs('#note-list');
    const notes = state.app.notes.filter((note) => state.ui.activeView === 'trash' ? note.deleted : !note.deleted).filter((note) => !state.ui.activeTag || note.tags.includes(state.ui.activeTag)).filter((note) => { const query = state.ui.searchQuery.trim().toLocaleLowerCase(); return !query || model_1.model.textForSearch(note).toLocaleLowerCase().includes(query); }).sort((a, b) => { if (a.pinned !== b.pinned)
        return a.pinned ? -1 : 1; if (state.ui.sortOrder === 'alphabetical')
        return a.title.localeCompare(b.title); if (state.ui.sortOrder === 'created')
        return b.createdAt - a.createdAt; return b.updatedAt - a.updatedAt; });
    list.innerHTML = notes.length ? notes.map((note) => `<article class="note-row ${state.ui.selectedNoteId === note.id ? 'selected' : ''}" data-note-id="${safeText(note.id)}" draggable="true"><div class="note-row-top"><div class="note-title">${safeText(note.title || model_1.model.titleFromHTML(note.bodyHTML))}</div><time>${safeText((0, model_1.formatTimestamp)(note.updatedAt))}</time></div><div class="note-snippet">${highlightText(model_1.model.titleFromHTML(note.bodyHTML), state.ui.searchQuery)}</div><div class="note-tags">${note.tags.slice(0, 3).map((tag) => `<button class="mini-tag" data-tag="${safeText(tag)}">#${safeText(tag)}</button>`).join('')}</div></article>`).join('') : `<div class="empty-list"><div class="empty-icon">${svgIcon('search')}</div><strong>No notes here</strong><span>Try a different search or create a note.</span></div>`;
    list.querySelectorAll('[data-note-id]').forEach((row) => { row.addEventListener('click', () => selectNote(row.dataset.noteId ?? '')); row.addEventListener('dragstart', (event) => { state.dragNote = model_1.model.getNote(row.dataset.noteId ?? '') ?? null; event.dataTransfer?.setData('text/plain', row.dataset.noteId ?? ''); event.dataTransfer?.effectAllowed = 'move'; row.classList.add('dragging'); }); row.addEventListener('dragend', () => { row.classList.remove('dragging'); state.dragNote = null; }); });
    list.querySelectorAll('[data-tag]').forEach((tag) => tag.addEventListener('click', () => { state.ui.activeTag = tag.dataset.tag ?? null; state.ui.searchQuery = ''; saveUI(); renderList(); renderListHeader(); }));
}
function highlightText(value, query) { if (!query.trim())
    return safeText(value || 'No additional text'); const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); return safeText(value).replace(new RegExp(`(${escaped})`, 'ig'), '<mark>$1</mark>'); }
function renderEditor() {
    const editorRoot = qs('#editor-pane');
    const note = model_1.model.getNote(state.ui.selectedNoteId ?? '') ?? null;
    state.selectedNote = note;
    if (!note) {
        editorRoot.innerHTML = `<div class="empty-editor"><div class="empty-mark">N</div><h2>Select or create a note</h2><p>Your writing space is ready. Pick a note from the list or start something new.</p><button class="primary-btn" id="empty-new-note">${svgIcon('plus')}<span>Create a note</span></button></div>`;
        qs('#empty-new-note')?.addEventListener('click', () => qs('#new-note')?.click());
        return;
    }
    const title = note.title || model_1.model.titleFromHTML(note.bodyHTML) || 'Untitled note';
    const notebookName = state.app.notebooks.find((item) => item.id === note.notebookId)?.name ?? 'Notes';
    if (!notebookName)
        return;
    const editorBody = `<div class="editor-topbar"><div class="mobile-brand">Notely</div><div class="editor-path">Notes <span>/</span> ${safeText(notebookName)}</div><button class="icon-btn" id="editor-more" title="More actions">${svgIcon('dots')}</button></div><div class="editor-scroll"><div class="editor-canvas"><div class="title-input-wrap"><input id="note-title" class="note-title-input" value="${safeText(title)}" placeholder="Untitled note" autocomplete="off" spellcheck="false"></div><div id="save-indicator" class="save-indicator"><span class="save-dot"></span><span>Saved</span></div><div class="toolbar" id="editor-toolbar">${toolbarButtons()}</div><div class="tags-bar"><span class="tag-label">Tags</span><div class="tag-input-wrap">#<input id="tag-input" placeholder="Add a tag" value=""></div></div><div class="editor-content" id="editor-content" contenteditable="true" data-note-id="${safeText(note.id)}">${safeText(note.bodyHTML)}</div><div class="editor-footer"><span>${note.tags.length ? note.tags.map((tag) => `<button class="tag-pill" data-editor-tag="${safeText(tag)}">#${safeText(tag)}</button>`).join('') : '<span class="footer-hint">Add tags with # in the bar above</span>'}</span><span>${(0, model_1.formatTimestamp)(note.updatedAt)}</span></div></div></div><div class="editor-actions" id="editor-actions"></div>`;
    editorRoot.innerHTML = editorBody;
    const content = qs('#editor-content');
    const titleInput = qs('#note-title');
    state.editor = new editor_1.Editor(content, titleInput);
    state.editor.load(note);
    titleInput.addEventListener('input', () => { if (state.editor)
        state.editor.setTitle(titleInput.value); });
    qs('#editor-more')?.addEventListener('click', () => { const actions = qs('#editor-actions'); if (actions.childElementCount) {
        actions.innerHTML = '';
        actions.classList.remove('open');
        return;
    } actions.innerHTML = `<button data-action="pin">${svgIcon('pin')}<span>Pin note</span></button><button data-action="move">${svgIcon('move')}<span>Move to…</span></button><button data-action="duplicate">${svgIcon('copy')}<span>Duplicate</span></button><button data-action="delete">${svgIcon('trash')}<span>Move to Trash</span></button><div class="action-divider"></div><button data-action="restore">${svgIcon('check')}<span>Restore</span></button><button data-action="hard-delete">${svgIcon('trash')}<span>Permanently delete</span></button>`; actions.classList.add('open'); actions.querySelectorAll('[data-action]').forEach((button) => button.addEventListener('click', () => editorAction(button.dataset.action ?? '', note.id))); });
    qs('#tag-input')?.addEventListener('keydown', (event) => { const input = event.target; if (event.key === 'Enter') {
        input.blur();
        model_1.model.addTag(note.id, input.value);
        state.app = (0, model_1.getApplication)();
        state.ui = (0, model_1.getUI)();
        renderAll();
        qs('#tag-input')?.focus();
    } });
    qs('.tags-bar')?.querySelectorAll('[data-editor-tag]').forEach((tag) => tag.addEventListener('click', () => { state.ui.activeTag = tag.dataset.editorTag ?? null; state.ui.searchQuery = ''; saveUI(); renderList(); renderListHeader(); }));
    content.addEventListener('click', (event) => { const target = event.target; if (target.matches('input[type="checkbox"]')) {
        const checkbox = target;
        checkbox.checked = !checkbox.checked;
        const label = checkbox.nextElementSibling;
        if (label)
            label.classList.toggle('checked', checkbox.checked);
        saveNoteBody();
        return;
    } });
    content.addEventListener('keydown', (event) => { if (event.key === 'Enter') {
        const selection = window.getSelection();
        const node = selection?.anchorNode instanceof Node ? selection.anchorNode : null;
        if (node) {
            let current = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
            if (current instanceof HTMLElement && current.matches('li')) {
                event.preventDefault();
                document.execCommand('insertParagraph');
                if (!current.textContent.trim())
                    current.remove();
            }
        }
    } if (event.key === 'Tab' && event.shiftKey && document.activeElement?.tagName === 'INPUT') {
        const input = document.activeElement;
        input.checked = false;
        saveNoteBody();
    } });
    content.addEventListener('input', () => saveNoteBody());
    window.addEventListener('keydown', (event) => { if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'z' && !event.shiftKey) {
        event.preventDefault();
        state.editor?.undo();
    } if ((event.metaKey || event.ctrlKey) && (event.key.toLowerCase() === 'y' || (event.key.toLowerCase() === 'z' && event.shiftKey))) {
        event.preventDefault();
        state.editor?.redo();
    } if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'b') {
        event.preventDefault();
        state.editor?.toggleFormat('bold');
    } if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'i') {
        event.preventDefault();
        state.editor?.toggleFormat('italic');
    } if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'u') {
        event.preventDefault();
        state.editor?.toggleFormat('underline');
    } });
    renderActions();
}
function saveNoteBody() { if (!state.selectedNote || !state.editor)
    return; const html = (0, model_1.sanitizeHTML)(state.editor.getHTML()); const title = state.editor.getTitle() || model_1.model.titleFromHTML(html); state.selectedNote.title = title; state.selectedNote.bodyHTML = html; state.selectedNote.updatedAt = Date.now(); state.selectedNote.tags = [...new Set(state.selectedNote.tags.map((tag) => tag.trim().toLowerCase()).filter(Boolean))]; persistApp(); const indicator = qs('#save-indicator'); if (indicator) {
    indicator.textContent = 'Saved';
    indicator.classList.add('saved');
} renderList(); }
function toolbarButtons() { const items = [['bold', '<b>B</b>', 'Bold'], ['italic', '<i>I</i>', 'Italic'], ['underline', '<u>U</u>', 'Underline'], ['strike', '<s>S</s>', 'Strikethrough'], ['code', '<code>&lt;/&gt;</code>', 'Inline code'], ['|', '', ''], ['h1', 'H1', 'Heading 1'], ['h2', 'H2', 'Heading 2'], ['h3', 'H3', 'Heading 3'], ['|', '', ''], ['ul', '<span class="toolbar-bullets">•••</span>', 'Bulleted list'], ['ol', '<span class="toolbar-number">1.</span>', 'Numbered list'], ['quote', '❝', 'Quote'], ['hr', '—', 'Horizontal rule']]; return items.map(([id, html, title]) => id === '|' ? '<span class="toolbar-divider"></span>' : `<button data-format="${id}" title="${title}">${html}</button>`).join(''); }
function renderActions() { const actions = qs('#editor-actions'); if (actions) {
    actions.classList.toggle('open', false);
    actions.innerHTML = '';
} }
function editorAction(action, noteId) { const note = model_1.model.getNote(noteId); if (!note)
    return; if (action === 'pin')
    model_1.model.updateNote(noteId, { pinned: !note.pinned });
else if (action === 'duplicate')
    model_1.model.duplicateNote(noteId);
else if (action === 'delete')
    model_1.model.deleteNote(noteId);
else if (action === 'restore')
    model_1.model.restoreNote(noteId);
else if (action === 'hard-delete')
    model_1.model.hardDeleteNote(noteId);
else if (action === 'move') {
    const destination = window.prompt('Move note to notebook', state.app.notebooks.find((item) => item.id === note.notebookId)?.name ?? 'Notes');
    const target = state.app.notebooks.find((item) => item.name === destination);
    if (target)
        model_1.model.moveNote(noteId, target.id);
} state.app = (0, model_1.getApplication)(); state.ui = (0, model_1.getUI)(); state.selectedNote = model_1.model.getNote(state.ui.selectedNoteId ?? '') ?? null; if (state.selectedNote)
    state.editor?.load(state.selectedNote); renderAll(); }
function selectNote(noteId) { state.ui.selectedNoteId = noteId; state.app = (0, model_1.getApplication)(); state.ui = (0, model_1.getUI)(); saveUI(); state.selectedNote = model_1.model.getNote(noteId) ?? null; if (state.selectedNote && state.editor) {
    state.editor.attach(noteId);
    state.editor.load(state.selectedNote);
} renderEditor(); renderList(); }
function bindLayout() { qs('#sidebar-collapse')?.addEventListener('click', () => { state.ui.layout.sidebarCollapsed = !state.ui.layout.sidebarCollapsed; saveUI(); renderAll(); }); const resizeHandle = qs('#sidebar-resize'); const listHandle = qs('#list-resize'); let resizing = null; window.addEventListener('pointermove', (event) => { if (!resizing)
    return; if (resizing.kind === 'sidebar')
    state.ui.layout.sidebarWidth = Math.min(model_1.TUNING.maxSidebarWidth, Math.max(model_1.TUNING.minSidebarWidth, resizing.startWidth + event.clientX - resizing.startX));
else
    state.ui.layout.listWidth = Math.min(model_1.TUNING.maxListWidth, Math.max(model_1.TUNING.minListWidth, resizing.startWidth + event.clientX - resizing.startX)); saveUI(); renderAll(); }); window.addEventListener('pointerup', () => { resizing = null; }); resizeHandle?.addEventListener('pointerdown', (event) => { resizing = { kind: 'sidebar', startX: event.clientX, startWidth: state.ui.layout.sidebarWidth }; event.preventDefault(); }); listHandle?.addEventListener('pointerdown', (event) => { resizing = { kind: 'list', startX: event.clientX, startWidth: state.ui.layout.listWidth }; event.preventDefault(); }); document.addEventListener('click', (event) => { const target = event.target; const formatButton = target.closest('[data-format]'); if (formatButton && state.editor) {
    const id = formatButton.dataset.format;
    if (id === 'bold')
        state.editor.toggleFormat('bold');
    else if (id === 'italic')
        state.editor.toggleFormat('italic');
    else if (id === 'underline')
        state.editor.toggleFormat('underline');
    else if (id === 'strike')
        state.editor.toggleFormat('strikeThrough');
    else if (id === 'h1')
        state.editor.setHeading(1);
    else if (id === 'h2')
        state.editor.setHeading(2);
    else if (id === 'h3')
        state.editor.setHeading(3);
    else if (id === 'ul')
        state.editor.toggleList('ul');
    else if (id === 'ol')
        state.editor.toggleList('ol');
    else if (id === 'quote')
        state.editor.toggleQuote();
    else if (id === 'hr')
        state.editor.addHorizontalRule();
    else if (id === 'code')
        state.editor.toggleCode();
} }); document.addEventListener('keydown', (event) => { if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {
    event.preventDefault();
    qs('#global-search')?.focus();
} if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'b' && document.activeElement?.tagName !== 'INPUT') {
    event.preventDefault();
    state.editor?.toggleFormat('bold');
} if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'i' && document.activeElement?.tagName !== 'INPUT') {
    event.preventDefault();
    state.editor?.toggleFormat('italic');
} if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'u' && document.activeElement?.tagName !== 'INPUT') {
    event.preventDefault();
    state.editor?.toggleFormat('underline');
} }); }
function bindTheme() { qs('#theme-toggle')?.addEventListener('click', () => { state.app.theme = state.app.theme === 'dark' ? 'light' : 'dark'; persistApp(); applyTheme(); }); }
function boot() { state.app = (0, model_1.getApplication)(); state.ui = (0, model_1.getUI)(); renderAll(); bindLayout(); bindTheme(); qs('#global-search')?.focus(); }
boot();
};

// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SANCTIONED_TAGS = exports.UI_KEY = exports.NOTEBOOK_ID = exports.THEME_KEY = exports.LAYOUT_KEY = exports.STORAGE_KEY = void 0;
exports.STORAGE_KEY = 'notely.data.v1';
exports.LAYOUT_KEY = 'notely.layout.v1';
exports.THEME_KEY = 'notely.theme.v1';
exports.NOTEBOOK_ID = 'notes-inbox';
exports.UI_KEY = 'notely.ui.v1';
exports.SANCTIONED_TAGS = ['p', 'br', 'h1', 'h2', 'h3', 'blockquote', 'ul', 'ol', 'li', 'strong', 'b', 'em', 'i', 'u', 's', 'code', 'hr', 'span'];
};

// ── module: src/model.ts ──
__mods["src/model.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.model = exports.TUNING = void 0;
exports.getApplication = getApplication;
exports.getUI = getUI;
exports.setTheme = setTheme;
exports.setUI = setUI;
exports.createNotebook = createNotebook;
exports.renameNotebook = renameNotebook;
exports.deleteNotebook = deleteNotebook;
exports.createNote = createNote;
exports.getNote = getNote;
exports.updateNote = updateNote;
exports.deleteNote = deleteNote;
exports.restoreNote = restoreNote;
exports.hardDeleteNote = hardDeleteNote;
exports.moveNote = moveNote;
exports.duplicateNote = duplicateNote;
exports.addTag = addTag;
exports.removeTag = removeTag;
exports.visibleNotes = visibleNotes;
exports.getNotebook = getNotebook;
exports.getNotebookNotes = getNotebookNotes;
exports.getAllTags = getAllTags;
exports.textForSearch = textForSearch;
exports.snippetFor = snippetFor;
exports.titleFromHTML = titleFromHTML;
exports.parseHTML = parseHTML;
exports.extractText = extractText;
exports.sanitizeHTML = sanitizeHTML;
exports.formatTimestamp = formatTimestamp;
const types_1 = require("./types");
exports.TUNING = {
    autosaveDelayMs: 280,
    snippetLength: 160,
    searchPreviewLength: 500,
    defaultSidebarWidth: 250,
    defaultListWidth: 310,
    minSidebarWidth: 180,
    maxSidebarWidth: 460,
    minListWidth: 210,
    maxListWidth: 620,
};
const DEFAULT_BODY = [
    '<h1>Welcome to Notely</h1>',
    '<p>This is a rich, fast, and private writing space. Select a note to begin, or create a new one.</p>',
    '<ul><li><strong>Organize</strong> notes into notebooks</li><li>Add tags to connect ideas</li><li>Try checklist items and markdown shortcuts</li></ul>',
    '<blockquote>Make the work feel effortless.</blockquote>',
].join('\n');
const seedApp = () => ({
    notes: [
        {
            id: 'note-welcome',
            title: 'Welcome to Notely',
            bodyHTML: DEFAULT_BODY,
            createdAt: 1735689600000,
            updatedAt: 1735689600000,
            pinned: true,
            deleted: false,
            notebookId: types_1.NOTEBOOK_ID,
            tags: ['getting-started'],
        },
        {
            id: 'note-marks',
            title: 'Editor style guide',
            bodyHTML: '<h1>Style guide</h1><p>Write clearly, then <strong>shape the emphasis</strong> with the toolbar.</p><blockquote>Clean tools make room for clear thinking.</blockquote>',
            createdAt: 1735603200000,
            updatedAt: 1735646400000,
            pinned: false,
            deleted: false,
            notebookId: types_1.NOTEBOOK_ID,
            tags: ['writing'],
        },
        {
            id: 'note-plan',
            title: 'Release checklist',
            bodyHTML: '<h2>Before launch</h2><ul><li>Review analytics</li><li>Send customer email</li><li>Archive old notes</li></ul>',
            createdAt: 1735516800000,
            updatedAt: 1735560000000,
            pinned: false,
            deleted: false,
            notebookId: types_1.NOTEBOOK_ID,
            tags: ['project'],
        },
    ],
    notebooks: [
        { id: types_1.NOTEBOOK_ID, name: 'Notes', createdAt: 1735430400000 },
        { id: 'book-work', name: 'Work', createdAt: 1735344000000 },
        { id: 'book-personal', name: 'Personal', createdAt: 1735257600000 },
        { id: 'book-research', name: 'Research', createdAt: 1735171200000 },
    ],
    theme: 'light',
    createdAt: 1735084800000,
});
let appState = loadApp();
let uiState = loadUI();
let saveTimer;
function isObject(value) {
    return typeof value === 'object' && value !== null;
}
function validSortOrder(value) {
    return value === 'modified' || value === 'created' || value === 'alphabetical';
}
function validView(value) {
    return value === 'notes' || value === 'all-notes' || value === 'trash';
}
function uid() {
    return `id-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
function clamp(value, min, max) {
    return Math.min(max, Math.max(min, value));
}
function getApplication() {
    return appState;
}
function getUI() {
    return uiState;
}
function setTheme(theme) {
    appState.theme = theme;
    saveApp();
}
function setUI(mutator) {
    mutator(uiState);
    saveUI();
}
function createNotebook(name = 'New notebook') {
    const clean = name.trim();
    if (!clean)
        throw new Error('Notebook name cannot be blank');
    const notebook = { id: uid(), name: clean, createdAt: Date.now() };
    appState.notebooks.push(notebook);
    saveApp();
    return notebook;
}
function renameNotebook(id, name) {
    const notebook = appState.notebooks.find((item) => item.id === id);
    if (!notebook)
        throw new Error('Notebook not found');
    const clean = name.trim();
    if (!clean)
        throw new Error('Notebook name cannot be blank');
    notebook.name = clean;
    saveApp();
}
function deleteNotebook(id) {
    if (id === types_1.NOTEBOOK_ID)
        throw new Error('The Notes notebook cannot be deleted');
    const index = appState.notebooks.findIndex((item) => item.id === id);
    if (index === -1)
        throw new Error('Notebook not found');
    appState.notebooks.splice(index, 1);
    appState.notes.forEach((note) => {
        if (note.notebookId === id) {
            note.deleted = true;
            note.updatedAt = Date.now();
        }
    });
    if (uiState.activeView === 'notes') {
        uiState.activeView = 'all-notes';
        uiState.selectedNoteId = null;
    }
    saveAll();
}
function createNote() {
    const timestamp = Date.now();
    const note = {
        id: uid(), title: '', bodyHTML: '', createdAt: timestamp, updatedAt: timestamp,
        pinned: false, deleted: false, notebookId: types_1.NOTEBOOK_ID, tags: [],
    };
    appState.notes.push(note);
    uiState.selectedNoteId = note.id;
    if (uiState.activeView !== 'notes')
        uiState.activeView = 'notes';
    saveAll();
    return note;
}
function getNote(id) {
    return appState.notes.find((note) => note.id === id);
}
function updateNote(id, patch) {
    const note = getNote(id);
    if (!note)
        return undefined;
    Object.assign(note, patch, { updatedAt: Date.now() });
    saveApp();
    return note;
}
function deleteNote(id) {
    const note = getNote(id);
    if (!note)
        return;
    note.deleted = true;
    note.updatedAt = Date.now();
    if (uiState.selectedNoteId === id)
        uiState.selectedNoteId = null;
    if (uiState.activeView === 'trash')
        uiState.activeView = 'all-notes';
    saveAll();
}
function restoreNote(id) {
    const note = getNote(id);
    if (!note)
        return;
    note.deleted = false;
    note.updatedAt = Date.now();
    uiState.selectedNoteId = id;
    if (uiState.activeView === 'trash')
        uiState.activeView = 'all-notes';
    saveAll();
}
function hardDeleteNote(id) {
    const index = appState.notes.findIndex((note) => note.id === id);
    if (index === -1)
        return;
    appState.notes.splice(index, 1);
    if (uiState.selectedNoteId === id)
        uiState.selectedNoteId = null;
    saveAll();
}
function moveNote(id, notebookId) {
    const note = getNote(id);
    if (!note)
        return;
    note.notebookId = notebookId;
    note.updatedAt = Date.now();
    saveAll();
}
function duplicateNote(id) {
    const source = getNote(id);
    if (!source)
        throw new Error('Note not found');
    const timestamp = Date.now();
    const copy = {
        ...source,
        id: uid(),
        title: `${source.title.replace(/ Copy$/, '')} Copy`,
        bodyHTML: source.bodyHTML,
        createdAt: timestamp,
        updatedAt: timestamp,
        pinned: false,
        deleted: false,
    };
    appState.notes.push(copy);
    uiState.selectedNoteId = copy.id;
    saveAll();
    return copy;
}
function addTag(id, tag) {
    const note = getNote(id);
    if (!note)
        throw new Error('Note not found');
    const clean = tag.trim().replace(/^#/, '').toLowerCase();
    if (!clean)
        return '';
    if (!note.tags.includes(clean))
        note.tags.push(clean);
    saveAll();
    return clean;
}
function removeTag(id, tag) {
    const note = getNote(id);
    if (!note)
        return;
    note.tags = note.tags.filter((item) => item !== tag);
    saveAll();
}
function visibleNotes() {
    const query = uiState.searchQuery.trim().toLocaleLowerCase();
    const tag = uiState.activeTag;
    return appState.notes
        .filter((note) => uiState.activeView === 'all-notes' || (note.deleted === (uiState.activeView === 'trash')))
        .filter((note) => !tag || note.tags.includes(tag))
        .filter((note) => {
        if (!query)
            return true;
        return extractText(note).toLocaleLowerCase().includes(query);
    })
        .sort((a, b) => {
        if (a.pinned !== b.pinned)
            return a.pinned ? -1 : 1;
        if (uiState.sortOrder === 'alphabetical')
            return a.title.localeCompare(b.title, undefined, { sensitivity: 'base' });
        if (uiState.sortOrder === 'created')
            return b.createdAt - a.createdAt;
        return b.updatedAt - a.updatedAt;
    });
}
function getNotebook(id) {
    return appState.notebooks.find((notebook) => notebook.id === id);
}
function getNotebookNotes(id) {
    return visibleNotes().filter((note) => note.notebookId === id);
}
function getAllTags() {
    const counts = new Map();
    appState.notes.forEach((note) => {
        if (note.deleted)
            return;
        note.tags.forEach((tag) => counts.set(tag, (counts.get(tag) ?? 0) + 1));
    });
    return [...counts.entries()].sort((a, b) => a[0].localeCompare(b[0], undefined, { sensitivity: 'base' })).map(([tag]) => tag);
}
function textForSearch(note) {
    return extractText(note);
}
function snippetFor(note) {
    const text = extractText(note).trim().replace(/\s+/g, ' ');
    return text.length > exports.TUNING.snippetLength ? `${text.slice(0, exports.TUNING.snippetLength).trim()}…` : text;
}
function titleFromHTML(bodyHTML) {
    const parsed = parseHTML(bodyHTML);
    const heading = parsed.querySelector('h1');
    const text = heading ? extractText(heading).trim() : '';
    return text || snippetFor({ id: '', title: '', bodyHTML, createdAt: 0, updatedAt: 0, pinned: false, deleted: false, notebookId: types_1.NOTEBOOK_ID, tags: [] });
}
function parseHTML(html) {
    const template = document.createElement('template');
    template.innerHTML = html;
    return template.content;
}
function extractText(node) {
    const cloned = node instanceof Node ? node.cloneNode(true) : parseHTML(node.bodyHTML);
    const searchable = cloned;
    searchable.querySelectorAll('script, style, meta, link, iframe, object, embed').forEach((element) => element.remove());
    return searchable.textContent ?? '';
}
function sanitizeHTML(input) {
    const fragment = parseHTML(input);
    sanitizeFragment(fragment);
    const serializer = new XMLSerializer();
    return serializer.serializeToString(fragment);
}
function sanitizeFragment(fragment) {
    Array.from(fragment.childNodes).forEach((node) => sanitizeNode(node));
}
function sanitizeNode(node) {
    if (node.nodeType === Node.TEXT_NODE)
        return;
    if (!(node instanceof HTMLElement)) {
        const textNode = document.createTextNode(node.textContent ?? '');
        const parent = node.parentElement ?? document.body;
        const index = Array.from(parent.childNodes).indexOf(node);
        parent.replaceChild(textNode, node);
        if (index >= 0 && parent.childNodes[index] !== textNode)
            parent.removeChild(textNode);
        return;
    }
    const element = node;
    const parent = element.parentElement ?? document.body;
    const index = Array.from(parent.childNodes).indexOf(element);
    const tagName = element.tagName.toLowerCase();
    const forbidden = new Set(['script', 'style', 'meta', 'link', 'iframe', 'object', 'embed', 'canvas', 'svg', 'math', 'template', 'form', 'input', 'button', 'textarea', 'select', 'table', 'thead', 'tbody', 'tr', 'th', 'td']);
    if (forbidden.has(tagName)) {
        replaceWithText(element);
        return;
    }
    if (tagName === 'div' || tagName === 'section' || tagName === 'article' || tagName === 'main' || tagName === 'aside' || tagName === 'header' || tagName === 'footer' || tagName === 'nav' || tagName === 'picture') {
        element.replaceWith(...Array.from(element.childNodes));
        return;
    }
    const allowed = new Set(['p', 'br', 'h1', 'h2', 'h3', 'blockquote', 'ul', 'ol', 'li', 'strong', 'b', 'em', 'i', 'u', 's', 'strike', 'code', 'hr', 'span']);
    if (!allowed.has(tagName)) {
        replaceWithText(element);
        return;
    }
    element.removeAttribute('class');
    element.removeAttribute('style');
    element.removeAttribute('id');
    element.removeAttribute('dir');
    element.removeAttribute('contenteditable');
    element.removeAttribute('spellcheck');
    element.removeAttribute('draggable');
    element.removeAttribute('role');
    element.removeAttribute('data-');
    element.removeAttribute('onclick');
    element.removeAttribute('onerror');
    element.removeAttribute('onload');
    element.removeAttribute('onmouseover');
    Array.from(element.attributes).forEach((attribute) => {
        if (!attribute.name.startsWith('data-') && attribute.name !== 'checked')
            element.removeAttribute(attribute.name);
    });
    Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
    if (element.children.length === 0 && element.textContent.trim() === '' && tagName !== 'br' && tagName !== 'hr')
        element.remove();
    if (index >= 0 && parent.childNodes[index] !== element)
        parent.removeChild(element);
}
function replaceWithText(element) {
    const parent = element.parentElement ?? document.body;
    const textNode = document.createTextNode(element.textContent ?? '');
    const index = Array.from(parent.childNodes).indexOf(element);
    parent.replaceChild(textNode, element);
    if (index >= 0 && parent.childNodes[index] !== textNode)
        parent.removeChild(textNode);
}
function formatTimestamp(timestamp) {
    const now = Date.now();
    const diff = now - timestamp;
    if (diff < 60000)
        return 'Just now';
    if (diff < 3600000)
        return `${Math.floor(diff / 60000)}m ago`;
    if (diff < 86400000)
        return `${Math.floor(diff / 3600000)}h ago`;
    if (diff < 7 * 86400000)
        return `${Math.floor(diff / 86400000)}d ago`;
    return new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', year: diff > 315360000 ? 'numeric' : undefined }).format(new Date(timestamp));
}
function loadApp() {
    try {
        const raw = localStorage.getItem(types_1.STORAGE_KEY);
        if (!raw)
            return seedApp();
        const parsed = JSON.parse(raw);
        const app = {
            notes: Array.isArray(parsed.notes) ? parsed.notes.filter(isValidNote).map(normalizeNote) : seedApp().notes,
            notebooks: Array.isArray(parsed.notebooks) ? parsed.notebooks.filter(isValidNotebook).map(normalizeNotebook) : seedApp().notebooks,
            theme: parsed.theme === 'dark' ? 'dark' : 'light',
            createdAt: typeof parsed.createdAt === 'number' ? parsed.createdAt : Date.now(),
        };
        if (!app.notebooks.some((notebook) => notebook.id === types_1.NOTEBOOK_ID))
            app.notebooks.unshift({ id: types_1.NOTEBOOK_ID, name: 'Notes', createdAt: app.createdAt });
        return app;
    }
    catch {
        return seedApp();
    }
}
function isValidNote(value) {
    return isObject(value) && typeof value.id === 'string' && typeof value.title === 'string' && typeof value.bodyHTML === 'string' && typeof value.createdAt === 'number' && typeof value.updatedAt === 'number' && typeof value.pinned === 'boolean' && typeof value.deleted === 'boolean' && typeof value.notebookId === 'string' && Array.isArray(value.tags) && value.tags.every((tag) => typeof tag === 'string');
}
function normalizeNote(note) {
    return { ...note, title: note.title || '', bodyHTML: sanitizeHTML(note.bodyHTML), tags: [...new Set(note.tags.map((tag) => tag.trim().toLowerCase()).filter(Boolean))] };
}
function isValidNotebook(value) {
    return isObject(value) && typeof value.id === 'string' && typeof value.name === 'string' && typeof value.createdAt === 'number';
}
function normalizeNotebook(notebook) {
    return { id: notebook.id, name: notebook.name || 'Untitled notebook', createdAt: notebook.createdAt };
}
function loadUI() {
    try {
        const raw = localStorage.getItem(types_1.UI_KEY);
        if (!raw)
            return { activeView: 'notes', selectedNoteId: appState.notes[0]?.id ?? null, sortOrder: 'modified', searchQuery: '', activeTag: null, layout: { sidebarCollapsed: false, sidebarWidth: exports.TUNING.defaultSidebarWidth, listWidth: exports.TUNING.defaultListWidth } };
        const parsed = JSON.parse(raw);
        const selected = typeof parsed.selectedNoteId === 'string' ? parsed.selectedNoteId : (appState.notes[0]?.id ?? null);
        const layout = parsed.layout && isObject(parsed.layout) ? {
            sidebarCollapsed: Boolean(parsed.layout.sidebarCollapsed),
            sidebarWidth: clamp(Number(parsed.layout.sidebarWidth) || exports.TUNING.defaultSidebarWidth, exports.TUNING.minSidebarWidth, exports.TUNING.maxSidebarWidth),
            listWidth: clamp(Number(parsed.layout.listWidth) || exports.TUNING.defaultListWidth, exports.TUNING.minListWidth, exports.TUNING.maxListWidth),
        } : { sidebarCollapsed: false, sidebarWidth: exports.TUNING.defaultSidebarWidth, listWidth: exports.TUNING.defaultListWidth };
        return { activeView: validView(parsed.activeView) ? parsed.activeView : 'notes', selectedNoteId: selected, sortOrder: validSortOrder(parsed.sortOrder) ? parsed.sortOrder : 'modified', searchQuery: typeof parsed.searchQuery === 'string' ? parsed.searchQuery : '', activeTag: typeof parsed.activeTag === 'string' ? parsed.activeTag : null, layout };
    }
    catch {
        return { activeView: 'notes', selectedNoteId: appState.notes[0]?.id ?? null, sortOrder: 'modified', searchQuery: '', activeTag: null, layout: { sidebarCollapsed: false, sidebarWidth: exports.TUNING.defaultSidebarWidth, listWidth: exports.TUNING.defaultListWidth } };
    }
}
function saveApp() {
    scheduleSave();
}
function scheduleSave() {
    if (saveTimer !== undefined)
        window.clearTimeout(saveTimer);
    saveTimer = window.setTimeout(() => {
        try {
            localStorage.setItem(types_1.STORAGE_KEY, JSON.stringify(appState));
        }
        catch { /* storage may be unavailable */ }
    }, exports.TUNING.autosaveDelayMs);
}
function saveUI() {
    try {
        localStorage.setItem(types_1.UI_KEY, JSON.stringify(uiState));
    }
    catch { /* storage may be unavailable */ }
}
function saveAll() {
    saveApp();
    saveUI();
}
exports.model = {
    get application() { return appState; },
    get ui() { return uiState; },
    createNotebook, renameNotebook, deleteNotebook, createNote, getNote, updateNote, deleteNote, restoreNote, hardDeleteNote, moveNote, duplicateNote, addTag, removeTag, visibleNotes, getNotebook, getNotebookNotes, getAllTags, textForSearch, snippetFor, titleFromHTML, sanitizeHTML, extractText, formatTimestamp, setTheme, setUI,
};
};

// ── module: src/editor.ts ──
__mods["src/editor.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Editor = void 0;
const model_1 = require("./model");
class Editor {
    constructor(content, title) {
        this.undoStack = [];
        this.redoStack = [];
        this.dirty = false;
        this.focused = false;
        this.onInput = () => { this.flush(); this.renderSaved('Saving…'); };
        this.onCursorChange = () => { if (this.focused)
            this.syncToolbar(); };
        this.onListEnter = (event) => {
            if (event.key !== 'Enter')
                return;
            const selection = window.getSelection();
            const node = selection?.anchorNode instanceof Node ? selection.anchorNode : null;
            if (!node || !this.content.contains(node))
                return;
            let current = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
            if (!(current instanceof HTMLElement))
                return;
            const listItem = current.closest('li');
            if (!listItem)
                return;
            event.preventDefault();
            if (listItem.textContent.trim() === '') {
                document.execCommand('insertParagraph');
                listItem.remove();
                return;
            }
            document.execCommand('insertParagraph');
        };
        this.onChecklistKeydown = (event) => {
            if (event.key === 'Tab' && event.shiftKey) {
                const checkbox = document.activeElement;
                if (checkbox?.type === 'checkbox') {
                    event.preventDefault();
                    checkbox.checked = false;
                    return;
                }
            }
            if (event.key === 'Tab') {
                const checkbox = document.activeElement;
                if (checkbox?.type === 'checkbox') {
                    event.preventDefault();
                    checkbox.checked = true;
                }
            }
        };
        this.content = content;
        this.title = title;
        this.content.setAttribute('spellcheck', 'true');
        this.content.addEventListener('input', this.onInput);
        this.content.addEventListener('focus', () => { this.focused = true; this.syncToolbar(); });
        this.content.addEventListener('keyup', this.onCursorChange);
        this.content.addEventListener('mouseup', this.onCursorChange);
        this.content.addEventListener('beforeinput', (event) => {
            if (event.inputType === 'insertParagraph' && document.queryCommandState('justifyLeft')) {
                event.preventDefault();
                this.insertListBreak();
            }
        });
        this.content.addEventListener('paste', (event) => {
            event.preventDefault();
            const clipboardText = event.clipboardData?.getData('text/plain') ?? '';
            const text = clipboardText || '';
            const pasteText = text || '';
            const pasteTextValue = pasteText || '';
            const pastedText = pasteTextValue || '';
            if (pastedText)
                document.execCommand('insertText', false, pastedText.replace(/\r\n?/g, '\n'));
            this.flush();
        });
    }
    load(note) {
        this.undoStack.length = 0;
        this.redoStack.length = 0;
        this.dirty = false;
        this.title.value = note.title;
        this.content.innerHTML = note.bodyHTML;
        this.renderSaved('Saved');
        this.syncToolbar();
    }
    attach(_noteId) { this.undoStack.length = 0; this.redoStack.length = 0; this.dirty = false; }
    setTitle(title) { if (this.title.value === title)
        return; this.title.value = title; this.touch(); }
    syncToolbar() {
        const state = this.getState();
        this.updateButton('bold', state.bold);
        this.updateButton('italic', state.italic);
        this.updateButton('underline', state.underline);
        this.updateButton('strike', state.strikethrough);
        this.updateButton('code', state.code);
        this.updateButton('h1', state.heading === 'h1');
        this.updateButton('h2', state.heading === 'h2');
        this.updateButton('h3', state.heading === 'h3');
        this.updateButton('ul', state.list === 'ul');
        this.updateButton('ol', state.list === 'ol');
        this.updateButton('quote', state.quote);
    }
    getState() {
        const selection = window.getSelection();
        let node = selection?.anchorNode ?? null;
        if (!node || !this.content.contains(node))
            return { active: false, bold: false, italic: false, underline: false, strikethrough: false, heading: 'none', list: 'none', quote: false, code: false };
        if (node.nodeType === Node.TEXT_NODE)
            node = node.parentNode ?? null;
        if (!(node instanceof Element))
            return { active: false, bold: false, italic: false, underline: false, strikethrough: false, heading: 'none', list: 'none', quote: false, code: false };
        let current = node;
        while (current && this.content.contains(current)) {
            if (current.getAttribute('data-check-item'))
                return { active: true, bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), heading: 'none', list: 'ul', quote: false, code: false };
            if (current.matches('h1'))
                return { active: true, bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), heading: 'h1', list: 'none', quote: false, code: false };
            if (current.matches('h2'))
                return { active: true, bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), heading: 'h2', list: 'none', quote: false, code: false };
            if (current.matches('h3'))
                return { active: true, bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), heading: 'h3', list: 'none', quote: false, code: false };
            if (current.matches('blockquote'))
                return { active: true, bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), heading: 'none', list: 'none', quote: true, code: false };
            if (current.matches('ul'))
                return { active: true, bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), heading: 'none', list: 'ul', quote: false, code: false };
            if (current.matches('ol'))
                return { active: true, bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), heading: 'none', list: 'ol', quote: false, code: false };
            const parent = current.parentElement;
            current = parent ?? null;
        }
        return { active: false, bold: false, italic: false, underline: false, strikethrough: false, heading: 'none', list: 'none', quote: false, code: false };
    }
    updateButton(id, active) {
        const button = document.querySelector(`[data-format="${id}"]`);
        if (button)
            button.classList.toggle('active', Boolean(active));
    }
    touch() { this.flush(); this.renderSaved('Saving…'); }
    flush() { const snapshot = this.html(); if (snapshot === this.undoStack[this.undoStack.length - 1])
        return; this.undoStack.push(snapshot); if (this.undoStack.length > 100)
        this.undoStack.shift(); this.redoStack.length = 0; this.dirty = true; }
    html() { const clone = this.content.cloneNode(true); clone.querySelectorAll('input').forEach((input) => input.remove()); return (0, model_1.sanitizeHTML)(clone.innerHTML); }
    insertListBreak() { const fragment = '<div><br></div>'; if (fragment)
        document.execCommand('insertHTML', false, fragment); }
    renderSaved(status) {
        const indicator = document.querySelector('#save-indicator');
        if (indicator) {
            const message = status || 'Saved';
            if (message) {
                indicator.textContent = message;
                indicator.classList.toggle('saved', message === 'Saved');
            }
        }
    }
    toggleFormat(command) { this.content.focus(); document.execCommand(command, false, null); this.flush(); this.renderSaved('Saving…'); this.syncToolbar(); }
    setHeading(level) { this.content.focus(); document.execCommand(level === 0 ? 'formatBlock' : 'formatBlock', false, level === 0 ? 'P' : `h${level}`); this.flush(); this.renderSaved('Saving…'); this.syncToolbar(); }
    toggleList(type) { this.content.focus(); document.execCommand(type === 'ul' ? 'insertUnorderedList' : 'insertOrderedList', false, null); this.flush(); this.renderSaved('Saving…'); this.syncToolbar(); }
    toggleQuote() { this.content.focus(); document.execCommand('formatBlock', false, 'blockquote'); this.flush(); this.renderSaved('Saving…'); this.syncToolbar(); }
    toggleCode() {
        this.content.focus();
        const selection = window.getSelection();
        const node = selection?.anchorNode instanceof Node ? selection.anchorNode : null;
        if (!selection || !selection.anchorNode || !node)
            return;
        let current = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
        while (current && this.content.contains(current)) {
            if (current instanceof HTMLElement && current.tagName.toLowerCase() === 'code') {
                const code = current;
                const textNode = document.createTextNode(code.textContent ?? '');
                code.replaceWith(textNode);
                selection?.collapse(textNode, 0);
                break;
            }
            current = current.parentElement;
        }
        if (selection.anchorNode.nodeType === Node.TEXT_NODE) {
            const text = selection.toString() ?? '';
            if (text) {
                const codeText = text.replace(/&/g, '&amp;').replace(/</g, '&lt;');
                if (codeText)
                    document.execCommand('insertHTML', false, `<code>${codeText}</code>`);
            }
        }
        this.flush();
        this.renderSaved('Saving…');
        this.syncToolbar();
    }
    toggleChecklist() { this.content.focus(); document.execCommand('insertHTML', false, '<div><span data-check-item class="check-item"><input type="checkbox" aria-label="Checklist item"> <span class="check-label"></span></span></div>'); this.flush(); this.renderSaved('Saving…'); this.syncToolbar(); }
    addHorizontalRule() { this.content.focus(); document.execCommand('insertHorizontalRule'); this.flush(); this.renderSaved('Saving…'); }
    undo() { if (!this.undoStack.length)
        return; this.redoStack.push(this.html()); this.content.innerHTML = this.undoStack.pop() ?? ''; this.flush(); this.syncToolbar(); }
    redo() { if (!this.redoStack.length)
        return; this.undoStack.push(this.html()); this.content.innerHTML = this.redoStack.pop() ?? ''; this.flush(); this.syncToolbar(); }
    getHTML() { return this.html(); }
    getTitle() { return this.title.value.trim(); }
    get dirtyState() { return this.dirty; }
    get bodyText() { return (0, model_1.extractText)(this.content); }
}
exports.Editor = Editor;
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"bartowski/nex-agi_Nex-N2.5-mini-GGUF","provider":"brigid","persona":"typescript","sessionId":"1a05fcb7-3eb9-4aa4-b77e-df6516f1df3b","tokensIn":58350560,"tokensOut":198378,"tokensTotal":58548938,"cost":0,"turns":346,"toolCalls":345,"failedToolCalls":141,"timestamp":"2026-09-14T18:19:34.027Z"} -->