Monday, January 12, 2026

 <!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Capture</title>

<style>

html, body { height:100%; margin:0; padding:0; font-family: Inter, Roboto, Arial, sans-serif; }

body { display:flex; flex-direction:column; background:#f8f9fa; padding:20px; box-sizing:border-box; }

#top-bar { position: fixed; top: 10px; right: 10px; z-index:1000; }

#mode-toggle { font-size:22px; background:none; border:none; cursor:pointer; }

#gpc-display { font-weight:bold; margin-bottom:12px; font-size:18px; }

#gpc-buttons {

  display: flex;

  align-items: center;

  gap: 8px;        /* spacing between buttons */

  margin-bottom: 8px;

}


#gpc-buttons button {

  font-size: 1em;

  padding: 8px 12px;

  cursor: pointer;

  border-radius: 6px;

  border: 1px solid #ccc;

  background: #f9f9f9;

  transition: background 0.2s ease;

}


#gpc-buttons button:hover {

  background: #eee;

}

#gpc-buttons button.active { background:#111; color:#fff; border-color:#111; }

#editor-container { flex:1; display:flex; flex-direction:column; margin-bottom:60px; }


#editor, #preview { 

  flex:1; width:100%; box-sizing:border-box; 

  border:1px solid #ddd; 

  font-family:"Courier New", monospace; font-size:16px; line-height:1.4; background:#fff; overflow:auto; white-space: pre-wrap; 

}


#editor {

  display:block;

  padding:20px;

  border-radius:8px;

}


#preview { 

  display:none; 

  padding:20px;

  margin-left: -20px;

  margin-right: -20px;

  width: calc(100% + 40px);

  border-left: none;

  border-right: none;

  border-radius: 8px 8px 0 0;

}


#preview pre {

  position: relative;

  background: #f5f5f5;

  padding: 12px;

  border-radius: 6px;

  overflow-x: auto;

  font-family: "Courier New", monospace;

}

#preview .copy-btn {

  z-index: 10;

}


#floating-t-dropdown {

  display: none;

  position: fixed;

  bottom: 60px;

  left: 50%;

  transform: translateX(-50%);

  background:#fff;

  border:1px solid #ccc;

  border-radius:8px;

  box-shadow: 0 -8px 20px rgba(0,0,0,0.2);

  z-index: 999999999;

  min-width: 120px; /* same as H */

  padding: 0;       /* remove extra padding inside container */

}

#floating-t-dropdown button {

  width: 100%;

  padding: 8px 0;  /* match H button vertical padding */

  border: none;

  background: none;

  cursor: pointer;

  font-size: 16px; /* same as H */

  border-bottom: 1px solid #eee;

  text-align: center;

}

#floating-t-dropdown button:last-child {

  border-bottom: none;

}

#floating-t-dropdown.open { display: block; }

#floating-t-dropdown button:hover { background:#f0f0f0; }


#floating-table-dropdown {

  display:none;

  position:fixed;

  bottom:60px;

  left:50%;

  transform:translateX(-50%);

  background:#fff;

  border:1px solid #ccc;

  border-radius:8px;

  box-shadow:0 -8px 20px rgba(0,0,0,0.2);

  z-index:999999999;

  min-width:160px;

}

#floating-table-dropdown button {

  width:100%;

  padding:12px;

  border:none;

  background:none;

  cursor:pointer;

  border-bottom:1px solid #eee;

}

#floating-table-dropdown button:last-child { border-bottom:none; }

#floating-table-dropdown.open { display:block; }


/* Highlight */

#editor-container .highlight {

  background-color: yellow;

}

#preview .highlight {

  background-color: yellow;

}


/* Colored highlights */

#preview .highlight.pink { background-color: #ffc0cb !important; }

#preview .highlight.orange { background-color: #ffdab9 !important; }

#preview .highlight.yellow { background-color: #ffff99 !important; }

#preview .highlight.green { background-color: #90ee90 !important; }

#preview .highlight.blue { background-color: #add8e6 !important; }

#preview .highlight.purple { background-color: #e6ccff !important; }

#preview .highlight.gray { background-color: #d3d3d3 !important; }

#preview .highlight.brown { background-color: #d2b48c !important; }


/* Table styling */

#preview table {

  border-collapse: collapse;

  width: 100%;

  margin: 10px 0;

}


#preview th, #preview td {

  border: 1px solid #999;

  padding: 6px 10px;

  text-align: left;

}


#preview th {

  background-color: #f2f2f2;

}


#floating-highlight-dropdown {

  display: none; position: fixed; bottom: 60px; left: 50%;

  transform: translateX(-50%); background:#fff; border:1px solid #ccc;

  border-radius:8px; box-shadow: 0 -8px 20px rgba(0,0,0,0.2);

  z-index: 999999999; min-width: 120px;

}

#floating-highlight-dropdown button {

  display: block; width:100%; text-align:center; padding:12px;

  border:none; background:none; cursor:pointer; font-size:15px;

  border-bottom: 1px solid #eee;

}

#floating-highlight-dropdown button:last-child { border-bottom: none; }

#floating-highlight-dropdown button:hover { background:#f0f0f0; }

#floating-highlight-dropdown.open { display: block; }


/* Tighter bullets and checkboxes */

#preview ul, #preview ol {

  padding-left: 1.6em;

  margin: 0.4em 0;

}

#preview li {

  margin-bottom: 0.15em;

  line-height: 1.35;

}


/* Draggable unchecked tasks */

#preview li.task-unchecked {

  cursor: grab;

  padding: 3px 5px;

  border-radius: 5px;

  transition: background 0.2s;

}

#preview li.task-unchecked:active {

  cursor: grabbing;

}

#preview li.task-unchecked.dragging {

  opacity: 0.7;

  background: rgba(100, 149, 237, 0.4) !important;

  box-shadow: 0 4px 12px rgba(0,0,0,0.25);

  z-index: 10;

}

#preview li.task-unchecked.drag-over {

  background: rgba(100, 149, 237, 0.15);

  border-top: 2px solid cornflowerblue;

}


/* GPC stamps (DOY, DOS, DOM, Heptad) in Times New Roman */

#preview .gpc-stamp {

  font-family: "Times New Roman", Times, serif !important;

  text-decoration: none !important;

}


/* Gregorian date in Helvetica */

#preview .gregorian-stamp {

  font-family: Helvetica, Arial, sans-serif !important;

  text-decoration: none !important;

}


/* Block underline globally */

#preview u, #preview em, #preview strong {

  text-decoration: none !important;

}



#toolbar {

  display:none;

  position:fixed;

  bottom:0;

  left:0;

  width:100%;

  background:#eee;

  padding:6px 0;

  border-top:1px solid #ccc;

  z-index:1000;

  overflow-x:auto;

  -webkit-overflow-scrolling:touch;

}

#toolbar-inner {

  display:inline-flex;

  gap:8px;

  padding:0 10px;

  padding-right:140px;

  

}

#toolbar button {

  flex-shrink:0;

  padding:8px 12px;

  border-radius:6px;

  border:1px solid #aaa;

  cursor:pointer;

  background:#fff;

  font-size:16px;

}


/* Shared floating dropdown style */

#floating-h-dropdown, #floating-date-dropdown {

  display: none;

  position: fixed;

  bottom: 60px;

  left: 50%;

  transform: translateX(-50%);

  background:#fff;

  border:1px solid #ccc;

  border-radius:8px;

  box-shadow: 0 -8px 20px rgba(0,0,0,0.2);

  z-index: 999999999;

  min-width: 120px;

}

#floating-h-dropdown button, #floating-date-dropdown button {

  display: block;

  width:100%;

  text-align:center;

  padding:12px;

  border:none;

  background:none;

  cursor:pointer;

  font-size:15px;

  border-bottom: 1px solid #eee;

}

#floating-h-dropdown button:last-child, #floating-date-dropdown button:last-child {

  border-bottom: none;

}

#floating-h-dropdown button:hover, #floating-date-dropdown button:hover { background:#f0f0f0; }

#floating-h-dropdown.open, #floating-date-dropdown.open { display: block; }

/* Cursor pointer for headings */

#preview h1, #preview h2, #preview h3, #preview h4, #preview h5, #preview h6 {

  cursor: pointer;

  user-select: none;

  position: relative;

}


/* Indent subheadings */

#preview h2 { margin-left: 1.2em; }

#preview h3 { margin-left: 2.4em; }

#preview h4 { margin-left: 3.6em; }

#preview h5 { margin-left: 4.8em; }

#preview h6 { margin-left: 6em; }


/* Hide nested content */

.collapsed + .collapsible-content {

  display: none;

}

/* Arrow indicator for collapsible headings */

h1, h2, h3, h4, h5, h6 {

  cursor: pointer;

  user-select: none;

  position: relative;

  padding-left: 1.2em; /* space for the arrow */

}


h1::before,

h2::before,

h3::before,

h4::before,

h5::before,

h6::before {

  content: '▶'; /* arrow symbol */

  position: absolute;

  left: 0;

  transition: transform 0.2s ease;

  font-size: 0.9em;

  top: 0.05em; /* adjust vertical alignment */

}


h1.collapsed::before,

h2.collapsed::before,

h3.collapsed::before,

h4.collapsed::before,

h5.collapsed::before,

h6.collapsed::before {

  transform: rotate(0deg); /* point right when collapsed */

}


h1:not(.collapsed)::before,

h2:not(.collapsed)::before,

h3:not(.collapsed)::before,

h4:not(.collapsed)::before,

h5:not(.collapsed)::before,

h6:not(.collapsed)::before {

  transform: rotate(90deg); /* point down when expanded */

}

/* Zen backlinks - subtle underline + hover */

#preview .backlinks {

  margin: 2em 0 1em 0;

  padding: 0.5em 0;

  border-top: 1px solid #e5e5e5;

}


#preview .backlinks h4 {

  margin: 0 0 0.8em 0;

  font-size: 1em;

  color: #666;

  font-weight: 400;

}


#preview .backlinks ul {

  margin: 0;

  padding-left: 0;

  list-style: none;

}


#preview .backlinks li {

  margin: 0.4em 0;

}


#preview .backlinks a.backlink {

  color: #1a73e8;           /* Subtle blue */

  text-decoration: none;    /* No underline */

  font-weight: 500;

  padding: 0.1em 0.3em;

  border-radius: 3px;

  border-bottom: 1px solid transparent;  /* ← CLICKABLE CUE */

  transition: all 0.15s ease;

}


#preview .backlinks a.backlink:hover {

  background: rgba(26,115,232,0.08);

  border-bottom-color: #1a73e8;

  transform: translateX(1px);

}

/* Sidebar slides over content, invisible when closed */

#sidebar {

  width: 250px;

  background: #f0f0f0;

  position: fixed;       /* float over content */

  top: 0;                /* all the way to the top now */

  bottom: 0;

  left: -250px;          /* completely off-screen when closed */

  padding: 1em 1em 1em 1em; /* optional: top/right/bottom/left padding */

  overflow-y: auto;

  z-index: 100;          /* above content when open */

  box-shadow: 2px 0 6px rgba(0,0,0,0.2);

  transition: left 0.25s ease;

  pointer-events: none;  /* ignore clicks when closed */

  opacity: 0;            /* invisible when closed */

}


/* Sidebar open */

#sidebar.open {

  left: 0;

  pointer-events: auto;  /* interactable */

  opacity: 1;            /* visible */

}


/* Main content stays full width */

#main-content {

  flex: 1;

  display: flex;

  flex-direction: column;

}


/* Sidebar items styling */

#sidebar .sidebar-item {

  font-size: 1.2em;           /* bigger text */

  padding: 14px 20px;         /* bigger clickable area */

  margin-bottom: 12px;        /* spacing between buttons */

  border-radius: 8px;          /* subtle rounded corners */

  cursor: pointer;

  transition: background 0.2s ease, transform 0.1s ease;

  user-select: none;           /* prevent text selection on click */

}


/* Hover effect */

#sidebar .sidebar-item:hover {

  background: #e0e0e0;        /* subtle hover background */

  transform: translateX(2px); /* slight movement for feel */

}


/* Active / focused state */

#sidebar .sidebar-item.active {

  background: #d0d0d0;

  font-weight: 600;

}

.sidebar-header {

  display: flex;

  justify-content: space-between;

  align-items: center;

  padding: 12px 16px;

  border-bottom: 1px solid #ddd;

  background: #f8f8f8;

}


.sidebar-header h3 {

  margin: 0;

  font-size: 1.1em;

  color: #333;

}

/* Recent notes container */

#recent-list {

  margin-top: 10px;

  max-height: 350px; /* scroll if too many */

  overflow-y: auto;

  padding-left: 0;

  font-family: 'Inter', 'Roboto', sans-serif;

}


/* Each note item */

#recent-list .note-item {

  position: relative;

  padding: 10px 14px 10px 24px; /* space for bullet */

  margin: 4px 0;

  border-radius: 8px;

  cursor: pointer;

  font-size: 1em; /* slightly bigger */

  color: #1f2937; /* dark gray */

  background-color: #f9fafb; /* very light gray */

  transition: background 0.25s, transform 0.15s;

}


/* Zen-style bullet */

#recent-list .note-item::before {

  content: "•"; /* simple bullet */

  position: absolute;

  left: 10px;

  color: #6366f1; /* soft purple/blue */

  font-size: 1.2em;

  line-height: 1;

}


/* Hover effect */

#recent-list .note-item:hover {

  background-color: #eef2ff; /* light lavender */

  transform: translateX(2px);

}


/* Active note highlight */

#recent-list .note-item.active {

  background-color: #dbeafe; /* soft blue */

  color: #1e40af;

  font-weight: 500;

}


/* Scrollbar subtle */

#recent-list::-webkit-scrollbar {

  width: 5px;

}

#recent-list::-webkit-scrollbar-thumb {

  background-color: #c7d2fe;

  border-radius: 3px;

}

.note-item.active {

  background: #ffeb3b !important;

  font-weight: bold;

}

/* ===== Sidebar note items ===== */

.note-item {

  display: flex;

  justify-content: space-between; /* title left, trash right */

  align-items: center;

  padding: 4px 8px;

  cursor: pointer;

  border-bottom: 1px solid #e5e5e5; /* optional separator */

  transition: background 0.2s;

}


/* Note title: truncate if too long */

.note-title {

  overflow: hidden;

  text-overflow: ellipsis;

  white-space: nowrap;

  flex-grow: 1;       /* take all available space */

  margin-right: 8px;  /* spacing between title and trash */

}


/* Trash icon */

.note-item .delete-note {

  cursor: pointer;

  color: #c00;        /* dark red */

  font-size: 0.9em;   /* slightly smaller */

  user-select: none;  /* prevent accidental selection */

  margin-left: 8px;

}


/* Hover effects */

.note-item:hover {

  background: #f0f0f0;  /* subtle background hover */

}


.note-item:hover .delete-note {

  color: #f00;          /* bright red on hover */

}

/* Common flex layout for all note & folder items */

.note-item {

  display: flex;

  align-items: center;        /* vertically center everything */

  justify-content: space-between; /* title left, icons right */

  padding: 4px 8px;

  gap: 6px;

  cursor: pointer;

}


/* Note title / folder name */

.note-item .title {

  flex: 1;

  overflow: hidden;

  text-overflow: ellipsis;

  white-space: nowrap;

}


/* Icons (pencil, trash) */

.note-item .icons {

  display: flex;

  align-items: center;

  gap: 4px;

}


/* Optional: small size for icons */

.note-item .icons span {

  font-size: 0.9em;

  cursor: pointer;

}

</style>

</head>

<body>

</script>

<div id="top-bar">

  <button id="mode-toggle" title="View / Edit">✏️</button>

</div>


<div id="gpc-display"></div>


<div id="gpc-buttons">

  <button data-mode="DOY" class="active">DOY</button>

  <button data-mode="DOS">DOS</button>

  <button data-mode="DOM">DOM</button>

  <button data-mode="HEPTAD">Heptad</button>

  <button id="sidebar-toggle" title="Toggle Sidebar">📁</button> <!-- moved here -->

</div>


<div id="editor-container">

  <textarea id="editor" placeholder="Capture…"></textarea>

  <div id="preview"></div>

</div>



  

  <!-- Keep your other items if you want, or move them below -->

<div id="sidebar">

  <div class="sidebar-section">

    <div class="sidebar-item">↔ Forward / Back</div>

    <div class="sidebar-item">🔍 Search</div>

    

    <!-- Recent button -->

    <div class="sidebar-item" id="recent-btn">🗒️ Notes</div>

    <div id="recent-list" style="margin-top:8px;"></div>

    

    <div class="sidebar-item">⚙ System</div>

  </div>

</div>


<div id="toolbar">

  <div id="toolbar-inner">

    <button data-action="undo">↶</button>

<button data-action="redo">↷</button>

<button id="t-button">T ▼</button>

    <button data-action="highlight">🖍</button>

    <button id="highlight-button">🎨 ▼</button>

<button data-action="quote">❝</button> <!-- Quote block -->

<button data-action="code">📋</button> <!-- Inline code / backtick -->

    <button id="h-button">H ▼</button>

    <button id="date-button">📅 ▼</button>

    <button id="table-button">▦ ▼</button>

    <button data-action="ul">•</button>

    <button data-action="ol">1.</button>

    <button data-action="checkbox">☑</button>

    <button data-action="link">🔗</button>

    <button data-action="wikilink">📄</button>

    <button id="comment-btn">💬</button>

  </div>

</div>


<!-- H Dropdown -->

<div id="floating-h-dropdown">

  <button data-action="h1">H1</button>

  <button data-action="h2">H2</button>

  <button data-action="h3">H3</button>

  <button data-action="h4">H4</button>

  <button data-action="h5">H5</button>

  <button data-action="h6">H6</button>

</div>


<!-- Text Formatting Dropdown -->

<div id="floating-t-dropdown">

  <button data-action="bold"><b>B</b></button>

  <button data-action="italic"><i>I</i></button>

  <button data-action="underline"><u>U</u></button>

  <button data-action="strike">S</button>

  <button data-action="sub">X₂</button>

  <button data-action="sup">X²</button>

</div>


<!-- Date Stamp Dropdown -->

<div id="floating-date-dropdown">

  <button data-stamp="doy">DOY</button>

  <button data-stamp="dos">DOS</button>

  <button data-stamp="dom">DOM</button>

  <button data-stamp="heptad">Heptad</button>

  <button data-stamp="gregorian">Civil Date</button>

</div>


<div id="floating-table-dropdown">

  <button data-action="add-table">Add table</button>

  <button data-action="add-row">+ Row</button>

  <button data-action="del-row">- Row</button>

  <button data-action="add-col">+ Col</button>

  <button data-action="del-col">- Col</button>

  <button data-action="add-calc">🧮 Calculator</button>

  <button data-action="row-up">Row ↑</button>

<button data-action="row-down">Row ↓</button>

<button data-action="col-left">Col ←</button>

<button data-action="col-right">Col →</button>

</div>


<div id="floating-highlight-dropdown">

  <button data-color="pink">pink</button>

  <button data-color="orange">orange</button>

  <button data-color="yellow">yellow</button>

  <button data-color="green">green</button>

  <button data-color="blue">blue</button>

  <button data-color="purple">purple</button>

  <button data-color="gray">gray</button>

  <button data-color="brown">brown</button>

</div>


<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>

<script>

  // Safety guard: make sure VaultDB gets defined before we use it

  if (typeof VaultDB === 'undefined') {

    console.error("VaultDB definition missing — check script order!");

  }

</script>


<script>



/* =========================

   VAULT (IndexedDB)

   ========================= */

const VaultDB = (() => {

  const DB_NAME = "capture-vault";

  const STORE = "notes";

  const VERSION = 1;

  let db = null; // Start as null (explicitly)

  

  // Internal helper: wait until db is ready

  async function waitForDb() {

    if (db) return; // Already open → good

    if (!openPromise) {

      throw new Error("Database not initialized");

    }

    await openPromise; // Wait for open to finish

  }

  

  let openPromise = null; // Shared promise for opening

  

  function open() {

    if (openPromise) return openPromise; // Don't open twice

    

    openPromise = new Promise((resolve, reject) => {

      const req = indexedDB.open(DB_NAME, VERSION);

      req.onupgradeneeded = e => {

        const database = e.target.result;

        if (!database.objectStoreNames.contains(STORE)) {

          database.createObjectStore(STORE, { keyPath: "name" });

        }

      };

      req.onsuccess = () => {

        db = req.result;

        resolve();

      };

      req.onerror = () => reject(req.error);

    });

    

    return openPromise;

  }

  

  async function get(name) {

    await waitForDb(); // ← Safety: wait here

    return new Promise((resolve, reject) => {

      const tx = db.transaction(STORE, "readonly");

      const store = tx.objectStore(STORE);

      const req = store.get(name);

      req.onsuccess = () => resolve(req.result?.content || null);

      req.onerror = () => reject(req.error);

    });

  }

  

  async function set(name, content) {

    await waitForDb(); // ← Safety: wait here

    return new Promise((resolve, reject) => {

      const tx = db.transaction(STORE, "readwrite");

      tx.objectStore(STORE).put({ name, content });

      tx.oncomplete = () => resolve();

      tx.onerror = () => reject(tx.error);

    });

  }

  

  async function list() {

    await waitForDb(); // ← Safety: wait here

    return new Promise((resolve, reject) => {

      const tx = db.transaction(STORE, "readonly");

      const req = tx.objectStore(STORE).getAllKeys();

      req.onsuccess = () => resolve(req.result);

      req.onerror = () => reject(req.error);

    });

  }

  

  return { open, get, set, list };

})();


// === BASIC REFERENCES ===

const editor = document.querySelector("#editor");

const preview = document.querySelector("#preview");

let currentNote = null;


// === BACKLINKS SYSTEM ===

const backlinksMap = {}; // Stores which notes link to which


function updateBacklinks(currentNote, text) {

  for (const note in backlinksMap) {

    backlinksMap[note]?.delete(currentNote);

  }


  const matches = [...text.matchAll(/\[\[([^\|\]]+)(?:\|[^\]]+)?\]\]/g)];

  matches.forEach(m => {

    const linkedNote = m[1].trim();

    if (!backlinksMap[linkedNote]) backlinksMap[linkedNote] = new Set();

    backlinksMap[linkedNote].add(currentNote);

  });

}


function renderBacklinks(note) {

  const links = backlinksMap[note] ? Array.from(backlinksMap[note]) : [];

  if (links.length === 0) return '';


  return `

    <div class="backlinks">

      <h4>Backlinks</h4>

      <ul>

        ${links.map(n => `<li><a href="#" class="backlink" data-note="${n}">${n}</a></li>`).join('')}

      </ul>

    </div>

  `;

}




// === YOUR OTHER FUNCTIONS BELOW ===



</script>


<script>

document.addEventListener("DOMContentLoaded", function(){

  console.log("DOM loaded");

  


(async () => {

  try {

    console.log("Opening vault...");

    await VaultDB.open(); // ← this now returns a promise that waits

    await SidebarManager.init();

    console.log("Vault opened successfully");

    

    let content = await VaultDB.get("Home");

    if (!content) {

      content = "# Home\n";

      await VaultDB.set("Home", content);

    }

    

    editor.value = content;

    pushHistory(true);

    updatePreviewAndSave();

    

    console.log("Vault ready:", await VaultDB.list());

  } catch (err) {

    console.error("Vault startup failed:", err);

  }

})();



  


  const editor = document.getElementById("editor");

const preview = document.getElementById("preview");


// === Stable Undo / Redo System ===

const history = [];

let historyIndex = -1;

const HISTORY_LIMIT = 100;

let suppressHistory = false;


function pushHistory(force = false) {

  if (suppressHistory) return;

  

  const value = editor.value;

  

  if (!force && history[historyIndex] === value) return;

  

  history.splice(historyIndex + 1);

  history.push(value);

  

  if (history.length > HISTORY_LIMIT) {

    history.shift();

  } else {

    historyIndex++;

  }

}


function undo() {

  if (historyIndex <= 0) return;

  suppressHistory = true;

  historyIndex--;

  editor.value = history[historyIndex];

  suppressHistory = false;

  updatePreviewAndSave();

}


function redo() {

  if (historyIndex >= history.length - 1) return;

  suppressHistory = true;

  historyIndex++;

  editor.value = history[historyIndex];

  suppressHistory = false;

  updatePreviewAndSave();

}


editor.addEventListener("input", () => {

  pushHistory();          // typing

  updatePreviewAndSave();

});



  const toggleBtn = document.getElementById("mode-toggle");

  const toolbar = document.getElementById("toolbar");

  const hButton = document.getElementById("h-button");

  const dateButton = document.getElementById("date-button");

  const hDropdown = document.getElementById("floating-h-dropdown");

  const dateDropdown = document.getElementById("floating-date-dropdown");

  const tButton = document.getElementById("t-button");

const tDropdown = document.getElementById("floating-t-dropdown");

  let editMode = true;

  

  const highlightButton = document.getElementById("highlight-button");

const highlightDropdown = document.getElementById("floating-highlight-dropdown");


const tableButton = document.getElementById("table-button");

const tableDropdown = document.getElementById("floating-table-dropdown");


tableDropdown.addEventListener("mousedown", e => {

  e.preventDefault(); // keeps editor selection inside the table

});


const tableNavActions = [

  "row-up",

  "row-down",

  "col-left",

  "col-right"

];


function closeAllDropdowns() {

  hDropdown.classList.remove("open");

  tDropdown.classList.remove("open");

  dateDropdown.classList.remove("open");

  highlightDropdown.classList.remove("open");

  tableDropdown.classList.remove("open");

}


tableButton.addEventListener("click", e => {

  e.stopPropagation();

  hDropdown.classList.remove("open");

  tDropdown.classList.remove("open");

  dateDropdown.classList.remove("open");

  highlightDropdown.classList.remove("open");

  tableDropdown.classList.toggle("open");

});

tableDropdown.addEventListener("click", e => {

  const btn = e.target.closest("button[data-action]");

  if (!btn) return;

  

  const action = btn.dataset.action;

  

  ({

  "add-table": insertTable,

  "add-row": addRow,

  "del-row": deleteRow,

  "add-col": addColumn,

  "del-col": deleteColumn,

  "add-calc": addCalculator,

  "row-up": rowUp,

  "row-down": rowDown,

  "col-left": () => moveColumn("left"),

  "col-right": () => moveColumn("right")

})[action]?.();

  

  editor.focus();

  updatePreviewAndSave();

  tableDropdown.classList.remove("open");

});

  


  let currentNote = "Home";



  pushHistory(true);

  const html = marked.parse(editor.value);

preview.innerHTML = renderWikilinks(html);

  updatePreviewAndSave();


  function updateMode() {

  closeAllDropdowns(); // ← ADD THIS LINE

  

  if (editMode) {

    editor.style.display = "block";

    preview.style.display = "none";

    toolbar.style.display = "block";

    toggleBtn.textContent = "✏️";

  } else {

    editor.style.display = "none";

    preview.style.display = "block";

    toolbar.style.display = "none";

    toggleBtn.textContent = "👁";

  }

}

  

  

  

  

  updateMode();


  toggleBtn.addEventListener("click",()=>{ 

    editMode = !editMode; 

    updateMode(); 

  });


preview.addEventListener("click", async (e) => {

  const wikiLink = e.target.closest(".wikilink, .backlink");

  if (!wikiLink) return;

  

  e.preventDefault();

  

  const name = wikiLink.dataset.note?.trim();

  if (!name) return;

  

  // Safety check: VaultDB must be ready before we try to use it

  if (!VaultDB || typeof VaultDB.get !== 'function' || typeof VaultDB.set !== 'function') {

    console.error("VaultDB not ready yet when clicking wikilink");

    alert("Cannot switch note yet — storage is still loading. Wait 2–3 seconds and try again.");

    return;

  }

  

  try {

    // Save current note safely

    if (currentNote) {

      await VaultDB.set(currentNote, editor.value);

    }

    

    // Switch to new note

    currentNote = name;

    

    let content = await VaultDB.get(name);

    if (content === null || content === undefined) {

      content = `# ${name}\n\n`;

      await VaultDB.set(name, content);

    }

    

    editor.value = content;

    pushHistory(true);

    updatePreviewAndSave();

  } catch (err) {

    console.error("Error during note switch:", err);

    alert("Could not load the note — check console (F12) for details");

  }

});



  function updatePreviewAndSave() {

  // 1️⃣ Start with the raw Markdown

  const raw = editor.value;

  

  // 2️⃣ Apply wikilinks

  let html = renderWikilinks(raw);

  

  // 3️⃣ Obsidian-style highlights (FIXED)

html = html.replace(/==(\w+):([\s\S]+?)==/g, '<span class="highlight $1">$2</span>');

html = html.replace(/==([\s\S]+?)==/g, '<span class="highlight">$1</span>');

  

  // 4️⃣ Subscript / Superscript BEFORE Markdown

  html = html.replace(/~([\s\S]+?)~/g, '<sub>$1</sub>');

  html = html.replace(/\^([\s\S]+?)\^/g, '<sup>$1</sup>');

  

  // 5️⃣ Parse Markdown

  html = marked.parse(html);

  

  // 6️⃣ GPC stamps → Times New Roman

  const gpcPattern = /(🟠|🟤|🌺|⚫|🟢|🔵|🟣)\s\d{3}|Q\d \d{2}|\d{2}\.\d{2}|(♣️|♦️|♥️|♠️)\s*\d+/g;

  html = html.replace(gpcPattern, '<span class="gpc-stamp">$&</span>');

  

  // 7️⃣ Gregorian dates → Helvetica

  const gregPattern = /\d{4}-\d{2}-\d{2}/g;

  html = html.replace(gregPattern, '<span class="gregorian-stamp">$&</span>');

  

  // 8️⃣ Update preview

  // Update backlinks for current note

updateBacklinks(currentNote, raw);


// Show content + backlinks

preview.innerHTML = html + renderBacklinks(currentNote);


// Reapply collapsible headings

wrapHeadingsForCollapse();

  

  function wrapHeadingsForCollapse() {

  const headings = preview.querySelectorAll("h1, h2, h3, h4, h5, h6");


  headings.forEach((h) => {

    if (h.dataset.processed) return; // already processed


    const level = parseInt(h.tagName.substring(1));

    const contentWrapper = document.createElement("div");

    contentWrapper.className = "collapsible-content";


    // Move following siblings that are deeper or same nested

    let next = h.nextElementSibling;

    while (next) {

      if (next.tagName && next.tagName.startsWith("H")) {

        const nextLevel = parseInt(next.tagName.substring(1));

        if (nextLevel <= level) break;

      }

      const temp = next.nextElementSibling;

      contentWrapper.appendChild(next);

      next = temp;

    }


    h.after(contentWrapper);


    // Click toggle

    h.addEventListener("click", () => {

      h.classList.toggle("collapsed");

    });


    h.dataset.processed = true;

  });

}

  

  // ✅ FINAL ENHANCED TABLE CALCULATOR – Σ+ fully works, supports *, per-column ops, Remaining, smart formatting

preview.querySelectorAll("table").forEach((table) => {

  const rows = Array.from(table.rows);

  const lastRow = rows[rows.length - 1];

  

  // Utility: format numbers (whole if possible)

  function formatNumber(num) {

    return Number.isInteger(num) ? num : +num.toFixed(1);

  }

  

  // 🔹 Handle Σ row

  const labelCellText = lastRow.cells[0].innerText.trim();

  const match = labelCellText.match(/^Σ([+\-*])?$/);

  if (!match) return;

  

  const opSymbol = match[1];

  const globalOp = opSymbol || "+";

  let startCol = 1;

  let changeLabelToTotal = true;

  

  if (opSymbol) {

    startCol = 0;

    changeLabelToTotal = false;

  }

  

  const perColumnOps = Array.from(lastRow.cells).slice(1).map(cell => {

    const text = cell.innerText.trim();

    return (text === "+" || text === "-" || text === "*") ? text : globalOp;

  });

  

  const fullOps = startCol === 0 ? [globalOp, ...perColumnOps] : perColumnOps;

  

  // 🔹 Calculate Σ ignoring Remaining rows

  let sigmaTotal = null;

  for (let c = startCol; c < lastRow.cells.length; c++) {

    const values = [];

    for (let r = 1; r < rows.length - 1; r++) {

      const rowLabel = rows[r].cells[0]?.innerText.trim().toLowerCase();

      if (rowLabel === "remaining") continue;

      const val = parseFloat(rows[r].cells[c]?.innerText) || 0;

      values.push(val);

    }

    

    const op = fullOps[c - startCol];

    let result;

    if (op === "*") {

      result = values.length > 0 ? values.reduce((a, b) => a * b, 1) : 1;

    } else if (op === "-") {

      if (values.length === 0) result = 0;

      else if (values.length === 1) result = values[0];

      else result = values.reduce((a, b) => a - b);

    } else { // "+"

      result = values.reduce((a, b) => a + b, 0);

    }

    

    lastRow.cells[c].innerHTML = `<strong style="color: green;">${formatNumber(result)}</strong>`;

    if (sigmaTotal === null) sigmaTotal = result;

  }

  

  if (changeLabelToTotal) lastRow.cells[0].innerHTML = `<strong>total</strong>`;

  

  // 🔹 Handle Remaining rows with formulas like "44-@Σ", "44+@Σ", "44* @Σ" or "44@Σ"

  rows.forEach(row => {

    const label = row.cells[0]?.innerText.trim().toLowerCase();

    if (label === "remaining") {

      for (let c = 1; c < row.cells.length; c++) {

        const cell = row.cells[c];

        if (!cell) continue;

        

        const formula = cell.innerText.trim();

        const formulaMatch = formula.match(/([\d.]+)\s*([\+\-\*]?)\s*@Σ/);

        if (formulaMatch) {

          const num = parseFloat(formulaMatch[1]);

          const operator = formulaMatch[2] || "+";

          let remaining;

          

          if (operator === "+") remaining = num + sigmaTotal;

          else if (operator === "-") remaining = num - sigmaTotal;

          else if (operator === "*") remaining = num * sigmaTotal;

          

          cell.innerHTML = `<strong style="color: green;">${formatNumber(remaining)}</strong>`;

        }

      }

    }

  });

});

  

  // === STEP 2: Enable task list checkboxes ===


// Find marked-generated task checkboxes

const boxes = preview.querySelectorAll(

  'input[type="checkbox"][disabled]'

);


let taskIndex = 0;


boxes.forEach(box => {

  box.disabled = false;

  box.dataset.taskIndex = taskIndex++;

});


// Sync checkbox changes back to markdown

boxes.forEach(box => {

  box.addEventListener('change', () => {

    const index = Number(box.dataset.taskIndex);

    const lines = editor.value.split('\n');

    

    let current = -1;

    

    for (let i = 0; i < lines.length; i++) {

      if (/^- \[[ xX]\]/.test(lines[i])) {

        current++;

        if (current === index) {

          lines[i] = box.checked ?

            lines[i].replace('- [ ]', '- [x]') :

            lines[i].replace(/- \[[xX]\]/, '- [ ]');

          break;

        }

      }

    }

    

    editor.value = lines.join('\n');


// 🔽 SAFE inline reorder (no new functions)

const reordered = editor.value.split('\n');

let out = [], open = [], done = [];


for (let l of reordered) {

  if (/^\s*- \[[ xX]\]/.test(l)) {

    (/\[[xX]\]/.test(l) ? done : open).push(l);

  } else {

    out.push(...open, ...done);

    open = []; done = [];

    out.push(l);

  }

}

out.push(...open, ...done);


editor.value = out.join('\n');

updatePreviewAndSave();

  });

});


// 🔑 RTL: Fix Hebrew text (SPARING REGULAR COLORS) - COMPLETE VERSION

const rtlClass = 'custom-rtl-hebrew';

const style = document.createElement('style');

style.textContent = `

  .${rtlClass} {

    direction: rtl !important;

    text-align: right !important;

    unicode-bidi: isolate !important;

  }

  .${rtlClass} * {

    unicode-bidi: isolate;

  }

  /* Checkbox fix: keep checkbox on left even in RTL */

  .${rtlClass} li {

    display: flex;

    flex-direction: row-reverse;

    align-items: flex-start;

  }

  .${rtlClass} li input[type="checkbox"] {

    order: 2;

    margin-right: 0;

    margin-left: 8px;

    flex-shrink: 0;

  }

`;

document.head.appendChild(style);


function applyRTL(el) {

  const hasHebrew = /[֐-׿]/u.test(el.textContent || el.innerText);

  if (hasHebrew) {

    el.classList.add(rtlClass);

  } else {

    el.classList.remove(rtlClass);

  }

}


function refreshRTL() {

  const rtlElements = preview.querySelectorAll('p, li:not(.task-unchecked), td, th');

  rtlElements.forEach(applyRTL);

}


// Initial application

refreshRTL();


// Targeted observer - ONLY checkbox lists

const checkboxLists = preview.querySelectorAll('ul, ol');

const observer = new MutationObserver((mutations) => {

  let hasListChange = false;

  

  mutations.forEach((mutation) => {

    if (mutation.type === 'childList') {

      mutation.addedNodes.forEach((node) => {

        if (node.nodeType === 1 && (

            node.matches('li') ||

            node.matches('ul, ol') ||

            node.querySelector('li')

          )) {

          hasListChange = true;

        }

      });

    }

  });

  

  if (hasListChange) {

    // Small delay to let DOM settle after move

    setTimeout(refreshRTL, 10);

  }

});


// Observe only lists, not entire preview

checkboxLists.forEach(list => {

  observer.observe(list, { childList: true, subtree: true });

});


// Safety net: clean up non-Hebrew items every 500ms

setInterval(() => {

  preview.querySelectorAll('.custom-rtl-hebrew').forEach(el => {

    if (!/[֐-׿]/u.test(el.textContent || el.innerText)) {

      el.classList.remove('custom-rtl-hebrew');

    }

  });

}, 500);

  

  // 9️⃣ Save raw Markdown to localStorage

  VaultDB.set(currentNote, editor.value);

  // 10️⃣ Add copy buttons for code blocks

preview.querySelectorAll('pre').forEach(block => {

  // Avoid duplicating buttons

  if (block.querySelector('.copy-btn')) return;

  

  const btn = document.createElement('button');

  btn.textContent = '📋';

  btn.className = 'copy-btn';

  btn.style.position = 'absolute';

  btn.style.top = '4px';

  btn.style.right = '4px';

  btn.style.padding = '4px 6px';

  btn.style.fontSize = '14px';

  btn.style.cursor = 'pointer';

  btn.style.border = 'none';

  btn.style.background = '#eee';

  btn.style.borderRadius = '4px';

  

  btn.addEventListener('click', () => {

    navigator.clipboard.writeText(block.innerText).then(() => {

      btn.textContent = '✅';

      setTimeout(() => btn.textContent = '📋', 1000);

    });

  });

  

  block.style.position = 'relative';

  block.appendChild(btn);

});

// === MAKE UNCHECKED TASKS DRAGGABLE IN VIEW MODE (FIXED - NO DUPLICATES) ===

const taskLis = preview.querySelectorAll('li');

taskLis.forEach(li => {

  const checkbox = li.querySelector('input[type="checkbox"]');

  if (checkbox && !checkbox.checked) {

    li.classList.add('task-unchecked');

    li.draggable = true;

  } else {

    li.classList.remove('task-unchecked');

    li.draggable = false;

  }

});


// Remove old listeners to prevent duplicates on every refresh

preview.querySelectorAll('li.task-unchecked').forEach(item => {

  item.removeEventListener('dragstart', handleDragStart);

  item.removeEventListener('dragover', handleDragOver);

  item.removeEventListener('dragleave', handleDragLeave);

  item.removeEventListener('dragend', handleDragEnd);

  item.removeEventListener('drop', handleDrop);

});


// Shared dragged reference

let dragged = null;


// Event handlers (defined once)

function handleDragStart(e) {

  dragged = this;

  this.classList.add('dragging');

  e.dataTransfer.effectAllowed = 'move';

}


function handleDragOver(e) {

  if (dragged && dragged !== this && dragged.parentNode === this.parentNode) {

    e.preventDefault();

    this.classList.add('drag-over');

  }

}


function handleDragLeave() {

  this.classList.remove('drag-over');

}


function handleDragEnd() {

  if (dragged) dragged.classList.remove('dragging');

  preview.querySelectorAll('li.drag-over').forEach(el => el.classList.remove('drag-over'));

}


function handleDrop(e) {

  e.preventDefault();

  this.classList.remove('drag-over');

  

  if (!dragged || dragged === this || dragged.parentNode !== this.parentNode) return;

  

  const listContainer = this.parentNode;

  const allUncheckedLis = Array.from(listContainer.querySelectorAll('li.task-unchecked'));

  

  const fromIndex = allUncheckedLis.indexOf(dragged);

  const toIndex = allUncheckedLis.indexOf(this);

  

  // Move in DOM

  if (fromIndex < toIndex) {

    listContainer.insertBefore(dragged, this.nextSibling);

  } else {

    listContainer.insertBefore(dragged, this);

  }

  

  // === ACCURATELY REBUILD MARKDOWN FOR THIS LIST ONLY ===

  const lines = editor.value.split('\n');

  const newLines = [];

  let currentTaskBlock = [];

  let inTaskBlock = false;

  

  for (const line of lines) {

    if (/^\s*- \[[ xX]\] /.test(line)) {

      if (!inTaskBlock) {

        if (currentTaskBlock.length > 0) {

          newLines.push(...currentTaskBlock);

          currentTaskBlock = [];

        }

        inTaskBlock = true;

      }

      currentTaskBlock.push(line);

    } else {

      if (inTaskBlock) {

        // Process the completed task block

        const newOrder = Array.from(listContainer.children).map(li => {

  const checkbox = li.querySelector('input[type="checkbox"]');

  const isChecked = checkbox ? checkbox.checked : false;

  const rawText = li.textContent.trim();

  

  return currentTaskBlock.find(l => {

    const checkedInMd = /\[[xX]\]/.test(l);

    let taskText = l.replace(/^\s*- \[[ xX]\] /, '');

    let normalized = taskText

      .replace(/==(?:\w+:)?([\s\S]*?)==/g, '$1') // highlights (colored + plain)

      .replace(/~([\s\S]*?)~/g, '$1') // subscript

      .replace(/\^([\s\S]*?)\^/g, '$1') // superscript

      .replace(/\*\*([\s\S]*?)\*\*/g, '$1') // bold **

      .replace(/__([\s\S]*?)__/g, '$1') // bold __

      .replace(/\*([\s\S]*?)\*/g, '$1') // italic *

      .replace(/_([\s\S]*?)_/g, '$1') // italic _

      .replace(/~~([\s\S]*?)~~/g, '$1') // strikethrough

      .replace(/`([^`]*?)`/g, '$1') // inline code

      .replace(/\[([^\]]*?)\]\([^\)]*?\)/g, '$1') // links [text](url)

      .trim();

    

    return checkedInMd === isChecked && normalized === rawText;

  });

}).filter(Boolean);

        

        newLines.push(...newOrder);

        currentTaskBlock = [];

        inTaskBlock = false;

      }

      newLines.push(line);

    }

  }

  

  // Handle last block if file ends with tasks

  if (currentTaskBlock.length > 0) {

    const newOrder = Array.from(listContainer.children).map(li => {

      const checkbox = li.querySelector('input[type="checkbox"]');

      const isChecked = checkbox ? checkbox.checked : false;

      const rawText = li.textContent.trim();

      return currentTaskBlock.find(l => {

        const checkedInMd = /\[[xX]\]/.test(l);

        const textInMd = l.replace(/^\s*- \[[ xX]\] /, '').trim();

        return checkedInMd === isChecked && textInMd === rawText;

      });

    }).filter(Boolean);

    newLines.push(...newOrder);

  }

  

  editor.value = newLines.join('\n');

  updatePreviewAndSave(); // recursive but safe — only refreshes once

}


// Attach fresh listeners

preview.querySelectorAll('li.task-unchecked').forEach(item => {

  item.addEventListener('dragstart', handleDragStart);

  item.addEventListener('dragover', handleDragOver);

  item.addEventListener('dragleave', handleDragLeave);

  item.addEventListener('dragend', handleDragEnd);

  item.addEventListener('drop', handleDrop);

});

}

  


  function renderWikilinks(text) {

  return text.replace(

    /\[\[([^\|\]]+)(?:\|([^\]]+))?\]\]/g,

    (m, page, alias) => {

      const note = page.trim();

      const label = alias ? alias.trim() : note;

      return `<a href="#" class="wikilink" data-note="${note}">${label}</a>`;

    }

  );

}

  

  function insertTable() {

  const tpl =

    `|   |   |  

|---|---|  

|   |   |`;

  

  const pos = editor.selectionStart;

  editor.value =

    editor.value.slice(0, pos) +

    "\n" + tpl + "\n" +

    editor.value.slice(pos);

  

  editor.selectionStart = editor.selectionEnd = pos + tpl.length + 2;

}


// --- Table Utilities ---

function getTableAtCursor() {

  const lines = editor.value.split("\n");

  let row = editor.value.substr(0, editor.selectionStart).split("\n").length - 1;

  

  // Find nearest table line

  while (row >= 0 && !lines[row].includes("|")) row--;

  if (row < 0) return null;

  

  // Table start

  let start = row;

  while (start > 0 && lines[start - 1].includes("|")) start--;

  // Table end

  let end = row;

  while (end < lines.length - 1 && lines[end + 1].includes("|")) end++;

  

  return { lines, start, end };

}


function insertTable() {

  const tpl = "|   |   |\n|---|---|\n|   |   |";

  const pos = editor.selectionStart;

  editor.value = editor.value.slice(0, pos) + "\n" + tpl + "\n" + editor.value.slice(pos);

  editor.selectionStart = editor.selectionEnd = pos + 1;

  updatePreviewAndSave();

}


function addRow() {

  const t = getTableAtCursor();

  if (!t) return;

  

  // Find which row the cursor is currently on

  const cursorLine = editor.value.substring(0, editor.selectionStart).split('\n').length - 1;

  let insertAt = cursorLine; // default to current row

  

  // If cursor is on or below the separator row, insert after current row

  if (cursorLine <= t.start + 1) {

    insertAt = t.start + 2; // insert as first data row (after header + separator)

  }

  

  const headerCols = t.lines[t.start].split("|").length - 2;

  const newRow = "| " + Array(headerCols).fill("   ").join(" | ") + " |";

  

  modifyTableWithCursor(() => {

    t.lines.splice(insertAt + 1, 0, newRow); // insert AFTER the current row

    editor.value = t.lines.join("\n");

    updatePreviewAndSave();

  });

}


function deleteRow() {

  const t = getTableAtCursor();

  if (!t) return;

  if (t.end - t.start < 2) return; // prevent deleting header + separator

  

  const cursorLine = editor.value.substring(0, editor.selectionStart).split('\n').length - 1;

  

  // Don't allow deleting header or separator row

  if (cursorLine <= t.start + 1) return;

  

  modifyTableWithCursor(() => {

    t.lines.splice(cursorLine, 1);

    editor.value = t.lines.join("\n");

    updatePreviewAndSave();

  });

}


function addColumn() {

  const t = getTableAtCursor();

  if (!t) return;

  

  // Determine which column the cursor is in

  const beforeCursor = editor.value.substring(0, editor.selectionStart);

  const currentLineIndex = beforeCursor.split('\n').length - 1;

  const currentLine = t.lines[currentLineIndex];

  const offsetInLine = editor.selectionStart - (beforeCursor.lastIndexOf('\n') + 1);

  const colIndex = currentLine.substring(0, offsetInLine).split("|").length - 2;

  

  modifyTableWithCursor(() => {

    for (let i = t.start; i <= t.end; i++) {

      let parts = t.lines[i].split("|");

      const insertPos = colIndex + 1; // insert AFTER current column

      

      if (i === t.start + 1) {

        // Separator row

        parts.splice(insertPos + 1, 0, " --- ");

      } else {

        // Normal row

        parts.splice(insertPos + 1, 0, "   ");

      }

      t.lines[i] = parts.join("|");

    }

    editor.value = t.lines.join("\n");

    updatePreviewAndSave();

  });

}


function deleteColumn() {

  const t = getTableAtCursor();

  if (!t) return;

  

  const beforeCursor = editor.value.substring(0, editor.selectionStart);

  const currentLineIndex = beforeCursor.split('\n').length - 1;

  const currentLine = t.lines[currentLineIndex];

  const offsetInLine = editor.selectionStart - (beforeCursor.lastIndexOf('\n') + 1);

  const colIndex = currentLine.substring(0, offsetInLine).split("|").length - 2;

  

  // Prevent deleting if only 1 data column left

  const totalCols = t.lines[t.start].split("|").length - 2;

  if (totalCols <= 1) return;

  

  modifyTableWithCursor(() => {

    for (let i = t.start; i <= t.end; i++) {

      let parts = t.lines[i].split("|");

      const deletePos = colIndex + 1; // +1 because split("|") gives empty at ends

      parts.splice(deletePos, 1);

      t.lines[i] = parts.join("|");

    }

    editor.value = t.lines.join("\n");

    updatePreviewAndSave();

  });

}


function rowUp() {

  const t = getTableAtCursor();

  if (!t) return;

  const rowIndex = editor.value.substr(0, editor.selectionStart).split("\n").length - 1;

  if (rowIndex <= t.start + 1) return;

  

  modifyTableWithCursor(() => {

    const lines = t.lines;

    [lines[rowIndex - 1], lines[rowIndex]] = [lines[rowIndex], lines[rowIndex - 1]];

    editor.value = lines.join("\n");

    updatePreviewAndSave();

  });

}


function rowDown() {

  const t = getTableAtCursor();

  if (!t) return;

  const rowIndex = editor.value.substr(0, editor.selectionStart).split("\n").length - 1;

  if (rowIndex >= t.end) return;

  

  modifyTableWithCursor(() => {

    const lines = t.lines;

    [lines[rowIndex], lines[rowIndex + 1]] = [lines[rowIndex + 1], lines[rowIndex]];

    editor.value = lines.join("\n");

    updatePreviewAndSave();

  });

}


function moveColumn(dir) {

  const t = getTableAtCursor();

  if (!t) return;

  

  const beforeCursor = editor.value.substring(0, editor.selectionStart);

  const lineIndex = beforeCursor.split("\n").length - 1;

  const cursorInLine = editor.selectionStart - (beforeCursor.lastIndexOf("\n") + 1);

  const line = t.lines[lineIndex];

  const col = line.substring(0, cursorInLine).split("|").length - 2;

  const target = dir === "left" ? col - 1 : col + 1;

  if (target < 0) return;

  

  modifyTableWithCursor(() => {

    for (let i = t.start; i <= t.end; i++) {

      const cells = t.lines[i].split("|");

      if (!cells[col + 1] || !cells[target + 1]) continue;

      [cells[col + 1], cells[target + 1]] = [cells[target + 1], cells[col + 1]];

      t.lines[i] = cells.join("|");

    }

    editor.value = t.lines.join("\n");

    updatePreviewAndSave();

  });

}


function addCalculator() {

  const t = getTableAtCursor();

  if (!t) return;

  const headerCols = t.lines[t.start].split("|").length - 2;

  const ops = Array(headerCols).fill("+").join(" | ");

  const calcRow = "| Σ | " + ops + " |";

  

  modifyTableWithCursor(() => {

    t.lines.splice(t.end + 1, 0, calcRow);

    editor.value = t.lines.join("\n");

    updatePreviewAndSave();

  });

}


// ✅ In preview, sum/multiply columns if last row starts with Σ

function recalcTables() {

  preview.querySelectorAll("table").forEach(table => {

    const rows = Array.from(table.rows);

    const lastRow = rows[rows.length - 1];

    if (!lastRow.cells[0].innerText.trim().startsWith("Σ")) return;

    

    const ops = Array.from(lastRow.cells).slice(1).map(td => td.innerText.trim());

    

    for (let c = 1; c < ops.length + 1; c++) {

      const values = [];

      for (let r = 1; r < rows.length - 1; r++) {

        const cellText = rows[r].cells[c]?.innerText.trim();


// Skip formula rows like "44-@Σ"

if (cellText.includes('@Σ')) return;


const val = parseFloat(cellText);

if (!isNaN(val)) values.push(val);

      }

      lastRow.cells[c].innerText = ops[c - 1] === "*" ? values.reduce((a, b) => a * b, 1) :

        ops[c - 1] === "-" ? values.reduce((a, b) => a - b) :

        values.reduce((a, b) => a + b, 0);

    }

    // === Handle @Σ references (e.g. 44-@Σ) ===

rows.forEach((row, rIndex) => {

  if (rIndex === 0 || rIndex === rows.length - 1) return; // skip header & Σ row

  

  const cell = row.cells[startCol];

  if (!cell) return;

  

  const text = cell.innerText.trim();

  

  // Match: number - @Σ

  const match = text.match(/^([\d.]+)\s*-\s*@Σ$/);

  if (!match || sigmaTotal === null) return;

  

  const base = parseFloat(match[1]);

  if (isNaN(base)) return;

  

  const remaining = base - sigmaTotal;

  

  cell.innerHTML = `<strong style="color: green;">${remaining}</strong>`;

});

  });

}


// Helper: Run table modifications while preserving cursor position

function modifyTableWithCursor(callback) {

  const cursorPos = editor.selectionStart;

  const linesBefore = editor.value.substring(0, cursorPos).split('\n');

  const lineNumber = linesBefore.length - 1; // 0-based line index

  const columnInLine = cursorPos - (linesBefore.join('\n').length + (lineNumber > 0 ? 1 : 0));

  

  // Run the modification

  callback();

  

  // After update, try to restore cursor to same line and approximate column

  const newLines = editor.value.split('\n');

  if (lineNumber < newLines.length) {

    const newLineStart = newLines.slice(0, lineNumber).join('\n').length + (lineNumber > 0 ? 1 : 0);

    const targetPos = newLineStart + columnInLine;

    const clampedPos = Math.min(targetPos, newLines[lineNumber].length + newLineStart);

    editor.selectionStart = editor.selectionEnd = clampedPos;

  } else {

    // If lines were removed and cursor would be beyond end, go to end of document

    editor.selectionStart = editor.selectionEnd = editor.value.length;

  }

  

  editor.focus();

}


  // Auto-continue lists

  editor.addEventListener("keydown", (e) => {

  if (e.key !== "Enter" || e.shiftKey || e.ctrlKey || e.metaKey) return;

  

  const start = editor.selectionStart;

  const textBefore = editor.value.substring(0, start);

  const lineStart = textBefore.lastIndexOf("\n") + 1;

  const line = textBefore.substring(lineStart);

  

  const ulMatch = line.match(/^(\s*)- /);

  const olMatch = line.match(/^(\s*)\d+\. /);

  const taskMatch = line.match(/^(\s*)- \[[ xX]\] /);

  

  // 1. YOUR ORIGINAL SPECIAL EXIT for bullets/numbers with ( 

  if ((ulMatch || olMatch) && line.trim().match(/^-\s*\( |^\d+\.\s* \)/)) {

    e.preventDefault();

    const prefixLen = ulMatch ? "- ".length : olMatch[0].length;

    editor.value = editor.value.substring(0, lineStart) + editor.value.substring(lineStart + prefixLen);

    editor.selectionStart = editor.selectionEnd = lineStart;

    updatePreviewAndSave();

    return;

  }

  

  // 2. TASK LIST: exit if the task is empty (nothing after checkbox)

  if (taskMatch && line.substring(taskMatch[0].length).trim() === "") {

    e.preventDefault();

    editor.value = editor.value.substring(0, lineStart) + editor.value.substring(lineStart + taskMatch[0].length);

    editor.selectionStart = editor.selectionEnd = lineStart;

    updatePreviewAndSave();

    return;

  }

  

  // 3. TASK LIST: continue with new blank task

  if (taskMatch) {

    e.preventDefault();

    const indent = taskMatch[1];

    editor.value = editor.value.substring(0, start) + "\n" + indent + "- [ ] " + editor.value.substring(start);

    editor.selectionStart = editor.selectionEnd = start + indent.length + "- [ ] ".length + 1;

    updatePreviewAndSave();

    return;

  }

  

  // 4. YOUR ORIGINAL CONTINUE for normal bullets/numbers

  if (ulMatch || olMatch) {

    e.preventDefault();

    const indent = ulMatch ? ulMatch[1] : olMatch[1];

    const prefix = ulMatch ? "- " : "1. ";

    editor.value = editor.value.substring(0, start) + "\n" + indent + prefix + editor.value.substring(start);

    editor.selectionStart = editor.selectionEnd = start + indent.length + prefix.length + 1;

    updatePreviewAndSave();

  }

  });

  


  // Toolbar formatting

  toolbar.addEventListener("click", (e) => {

    let btn = e.target.closest("button[data-action]");

    if (!btn || !btn.dataset.action || ["h-button", "date-button", "highlight-button", "t-button"].includes(btn.id)) return;


    const action = btn.dataset.action;

    e.preventDefault();

    

    if (action === "undo") {

  undo();

  return;

}


if (action === "redo") {

  redo();

  return;

}


    



if (action === "checkbox") {

  const cursor = editor.selectionStart;

  

  // Find start of current line

  let lineStart = editor.value.lastIndexOf('\n', cursor - 1) + 1;

  if (lineStart < 0) lineStart = 0;

  

  // Find end of current line

  let lineEnd = editor.value.indexOf('\n', cursor);

  if (lineEnd === -1) lineEnd = editor.value.length;

  

  // Get current line text (with leading whitespace preserved)

  let lineText = editor.value.substring(lineStart, lineEnd);

  

  // Check if this line is already a task item (unchecked or checked)

  const taskRegex = /^\s*- \[[ xX]\] /;

  if (taskRegex.test(lineText)) {

    // It's a task → remove the checkbox prefix (toggle off)

    const newLine = lineText.replace(taskRegex, '');

    editor.value =

      editor.value.substring(0, lineStart) +

      newLine +

      editor.value.substring(lineEnd);

    

    // Place cursor at end of the new plain text

    editor.selectionStart = editor.selectionEnd = lineStart + newLine.length;

  } else {

    // Not a task → add checkbox at start

    const trimmedLine = lineText.trimStart(); // remove leading whitespace for clean insert

    const indent = lineText.substring(0, lineText.length - trimmedLine.length); // preserve indent

    const newLine = indent + `- [ ] ${trimmedLine}`;

    

    editor.value =

      editor.value.substring(0, lineStart) +

      newLine +

      editor.value.substring(lineEnd);

    

    // Place cursor right after the checkbox, ready to type

    editor.selectionStart = editor.selectionEnd = lineStart + indent.length + `- [ ] `.length;

  }

  

  editor.focus();

  updatePreviewAndSave();

  return;

}


    let start = editor.selectionStart;

    let end = editor.selectionEnd;

    const fullText = editor.value;


    if (start === end && ['bold','italic','underline','link','wikilink'].includes(action)) {

      const left = fullText.lastIndexOf(' ', start - 1) + 1;

      const right = fullText.indexOf(' ', start);

      start = left;

      end = right === -1 ? fullText.length : right;

    }


    const selected = fullText.substring(start, end);


    if (action === 'ul' || action === 'ol') {

      const prefix = action === 'ul' ? '- ' : '1. ';

      const lineStart = fullText.lastIndexOf('\n', start - 1) + 1;

      const lineEnd = fullText.indexOf('\n', end);

      const lineText = fullText.substring(lineStart, lineEnd === -1 ? undefined : lineEnd);

      const newLine = lineText.startsWith(prefix) ? lineText.substring(prefix.length) : prefix + lineText;

      editor.value = fullText.substring(0, lineStart) + newLine + fullText.substring(lineEnd === -1 ? fullText.length : lineEnd);

      editor.selectionStart = editor.selectionEnd = lineStart + newLine.length;

      editor.focus();

      updatePreviewAndSave();

      return;

    }


    let before = '', after = '';

    switch(action) {

  case 'bold': before = '**'; after = '**'; break;

  case 'italic': before = '*'; after = '*'; break;

  case 'underline': before = '<u>'; after = '</u>'; break;

  case 'link': before = '['; after = '](https://)'; break;

  case 'wikilink': before = '[['; after = ']]'; break;

  case 'highlight': before = '=='; after = '=='; break;

  case 'sub': before = '~'; after = '~'; break;

  case 'sup': before = '^'; after = '^'; break;

  case 'strike': before = '~~'; after = '~~'; break;

  case 'quote': 

    {

      // Add "> " at the start of the line

      const lineStart = fullText.lastIndexOf('\n', start - 1) + 1;

      const lineEnd = fullText.indexOf('\n', end);

      const lineText = fullText.substring(lineStart, lineEnd === -1 ? undefined : lineEnd);

      const newLine = lineText.startsWith('> ') ? lineText.substring(2) : '> ' + lineText;

      editor.value = fullText.substring(0, lineStart) + newLine + fullText.substring(lineEnd === -1 ? fullText.length : lineEnd);

      editor.selectionStart = editor.selectionEnd = lineStart + newLine.length;

      editor.focus();

      updatePreviewAndSave();

      return;

    }

  case 'code':

{

  const start = editor.selectionStart;

  const end = editor.selectionEnd;

  const selected = editor.value.substring(start, end) || "code here";

  const codeBlock = `\`\`\`\n${selected}\n\`\`\`\n`;

  editor.value = editor.value.substring(0, start) + codeBlock + editor.value.substring(end);

  editor.selectionStart = start + 4;

  editor.selectionEnd = start + 4 + selected.length;

  editor.focus();

  updatePreviewAndSave();

  return;

}

}


    const isWrapped = selected.startsWith(before) && selected.endsWith(after);

    const newSelected = isWrapped ? selected.slice(before.length, -after.length) : before + selected + after;


    editor.value = fullText.substring(0, start) + newSelected + fullText.substring(end);

    editor.selectionStart = editor.selectionEnd = start + (isWrapped ? 0 : before.length);

    editor.focus();

    updatePreviewAndSave();

  });


// H dropdown

hButton.addEventListener('click', (e) => {

  e.stopPropagation(); // prevent document click from closing immediately

  // Close all other dropdowns

  tDropdown.classList.remove('open');

  highlightDropdown.classList.remove('open');

  dateDropdown.classList.remove('open');

  tableDropdown.classList.remove('open');

  

  // Toggle H dropdown

  hDropdown.classList.toggle('open');

});

  tButton.addEventListener("click", (e) => {

  e.stopPropagation();

  hDropdown.classList.remove("open");

  highlightDropdown.classList.remove("open");

  dateDropdown.classList.remove("open");

  tableDropdown.classList.remove("open"); // ← ADD THIS

  tDropdown.classList.toggle("open");

});

// --- T dropdown formatting ---

tDropdown.addEventListener("click", (e) => {

  const btn = e.target.closest("button[data-action]");

  if (!btn) return;

  const action = btn.dataset.action;

  

  e.preventDefault();

  e.stopPropagation();

  

  const start = editor.selectionStart;

  const end = editor.selectionEnd;

  const fullText = editor.value;

  const selected = fullText.substring(start, end);


  let before = '', after = '';

  switch(action) {

    case 'bold': before='**'; after='**'; break;

    case 'italic': before='*'; after='*'; break;

    case 'underline': before='<u>'; after='</u>'; break;

    case 'strike': before='~~'; after='~~'; break;

    case 'sub': before='~'; after='~'; break;

    case 'sup': before='^'; after='^'; break;

  }

  const isWrapped = selected.startsWith(before) && selected.endsWith(after);

  const newSelected = isWrapped ? selected.slice(before.length,-after.length) : before + selected + after;

  editor.value = fullText.substring(0,start) + newSelected + fullText.substring(end);

  editor.selectionStart = editor.selectionEnd = start + (isWrapped ? 0 : before.length);

  editor.focus();

  updatePreviewAndSave();


  tDropdown.classList.remove("open");

});


  hDropdown.addEventListener('click', (e) => {

    let btn = e.target.closest("button[data-action]");

    if (!btn) return;

    if (!btn.dataset.action.startsWith('h')) return;


    e.preventDefault();

    e.stopPropagation();


    const level = btn.dataset.action.slice(1);

    const prefix = '#'.repeat(level) + ' ';

    const start = editor.selectionStart;

    const fullText = editor.value;

    const lineStart = fullText.lastIndexOf('\n', start - 1) + 1;

    const lineEnd = fullText.indexOf('\n', start);

    const lineText = fullText.substring(lineStart, lineEnd === -1 ? undefined : lineEnd);

    const newLine = lineText.startsWith(prefix) ? lineText.substring(prefix.length) : prefix + lineText;


    editor.value = fullText.substring(0, lineStart) + newLine + fullText.substring(lineEnd === -1 ? fullText.length : lineEnd);

    editor.selectionStart = editor.selectionEnd = lineStart + newLine.length;

    editor.focus();

    updatePreviewAndSave();


    hDropdown.classList.remove('open');

  });


// Date Stamp dropdown - FIXED

dateButton.addEventListener('click', (e) => {

  e.stopPropagation();

  tDropdown.classList.remove('open');

  hDropdown.classList.remove('open');

  highlightDropdown.classList.remove('open');

  tableDropdown.classList.remove('open'); // ← ADD THIS

  dateDropdown.classList.toggle('open');

});


const commentBtn = document.getElementById("comment-btn");


commentBtn.addEventListener("click", () => {

  const start = editor.selectionStart;

  const end = editor.selectionEnd;

  const selectedText = editor.value.slice(start, end);

  

  const commentText = `%%${selectedText}%%`;

  

  // Insert commentText at cursor, replacing selection

  editor.value = editor.value.slice(0, start) + commentText + editor.value.slice(end);

  

  // Move cursor inside the %%

  const cursorPos = start + 2; // between the %%

  editor.selectionStart = editor.selectionEnd = cursorPos + selectedText.length;

  

  editor.focus();

  

  // Trigger preview update if you have one

  updatePreviewAndSave();

});

  

  // Colored highlight dropdown

highlightButton.addEventListener('click', (e) => {

  e.stopPropagation();

  tDropdown.classList.remove('open');

  hDropdown.classList.remove('open');

  dateDropdown.classList.remove('open');

  tableDropdown.classList.remove('open'); // ← ADD THIS

  highlightDropdown.classList.toggle('open');

});


highlightDropdown.addEventListener('click', (e) => {

  const btn = e.target.closest("button[data-color]");

  if (!btn) return;

  const color = btn.dataset.color;

  e.preventDefault(); e.stopPropagation();


  const start = editor.selectionStart, end = editor.selectionEnd;

  const fullText = editor.value;

  let selected = fullText.substring(start, end) || "text";

  const wrapper = `==${color}:${selected}==`;

  editor.value = fullText.substring(0, start) + wrapper + fullText.substring(end);

  editor.selectionStart = start + 3 + color.length + 1;

  editor.selectionEnd = start + 3 + color.length + 1 + selected.length;

  editor.focus();

  updatePreviewAndSave();

  highlightDropdown.classList.remove('open');

});


  dateDropdown.addEventListener('click', (e) => {

    let btn = e.target.closest("button[data-stamp]");

    if (!btn) return;


    const type = btn.dataset.stamp;

    e.preventDefault();

    e.stopPropagation();


    const {doyToday} = calculateGPC();

    const weekEmoji = weekEmojis[(doyToday-1)%7] || '';


    let stamp = '';


    switch (type) {

  case 'doy':

    stamp = (weekEmoji ? weekEmoji + ' ' : '') + doyToday;

    if (heptadEnabled) {

      const heptad = heptadMap[doyToday] || '';

      if (heptad) stamp += ' ' + heptad;

    }

    break;

    

  case 'dos':

    const quarter = Math.floor((doyToday - 1) / 91) + 1;

    const dayInSeason = ((doyToday - 1) % 91) + 1;

    stamp = (weekEmoji ? weekEmoji + ' ' : '') + 'Q' + quarter + ' ' + String(dayInSeason).padStart(2, '0');

    if (heptadEnabled) {

      const heptad = heptadMap[doyToday] || '';

      if (heptad) stamp += ' ' + heptad;

    }

    break;

    

  case 'dom':

    let remaining = doyToday;

    let monthIndex = 0;

    for (let i = 0; i < gpcMonths.length; i++) {

      if (remaining <= gpcMonths[i].days) {

        monthIndex = i;

        break;

      }

      remaining -= gpcMonths[i].days;

    }

    const month = monthIndex + 1;

    const day = remaining;

    stamp = (weekEmoji ? weekEmoji + ' ' : '') + String(month).padStart(2, '0') + '.' + String(day).padStart(2, '0');

    if (heptadEnabled) {

      const heptad = heptadMap[doyToday] || '';

      if (heptad) stamp += ' ' + heptad;

    }

    break;

    

  case 'heptad':

    // ALWAYS insert the current heptad, even if toggle is off

    stamp = heptadMap[doyToday] || '';

    break;

    

  case 'gregorian':

    const today = new Date();

    const iso = today.toISOString().split('T')[0];

    stamp = iso;

    if (heptadEnabled) {

      const heptad = heptadMap[doyToday] || '';

      if (heptad) stamp += ' ' + heptad;

    }

    break;

}


    if (stamp) {

      const start = editor.selectionStart;

      const end = editor.selectionEnd;

      editor.value = editor.value.substring(0, start) + stamp + editor.value.substring(end);

      editor.selectionStart = editor.selectionEnd = start + stamp.length;

      editor.focus();

      updatePreviewAndSave();

    }


    dateDropdown.classList.remove('open');

  });


// Close dropdowns on outside click

document.addEventListener('click', (e) => {

  if (!hDropdown.contains(e.target) && e.target !== hButton) {

    hDropdown.classList.remove('open');

  }

  if (!dateDropdown.contains(e.target) && e.target !== dateButton) {

    dateDropdown.classList.remove('open');

  }

  if (!tDropdown.contains(e.target) && e.target !== tButton) {

    tDropdown.classList.remove('open');

  }

  if (!highlightDropdown.contains(e.target) && e.target !== highlightButton) {

    highlightDropdown.classList.remove('open');

  }

  if (!tableDropdown.contains(e.target) && e.target !== tableButton) { // ← ADD THIS

    tableDropdown.classList.remove('open');

  }

});


  // === YOUR FULL ORIGINAL GPC CALENDAR LOGIC - 100% UNTOUCHED ===

  const gpcMonths = [

    { name: 'Unspring', days: 30 },{ name: 'Duspring', days: 30 },{ name: 'Trispring', days: 31 },

    { name: 'Quadsum', days: 30 },{ name: 'Fivesum', days: 30 },{ name: 'Sixsum', days: 31 },

    { name: 'Sepafall', days: 30 },{ name: 'Oktafall', days: 30 },{ name: 'Novafall', days: 31 },

    { name: 'Dekawint', days: 30 },{ name: 'Elvawint', days: 30 },{ name: 'Dozawint', days: 31 }

  ];

  let baseMode = "DOY";

  let heptadEnabled = false;

  const gpcDisplayEl = document.getElementById("gpc-display");

  const gpcBtns = document.querySelectorAll("#gpc-buttons button[data-mode]");

  const epochDate = new Date(Date.UTC(1982,2,24));

  const epochYear = 5979;

  const weekEmojis = ['🟠','🟤','🌺','⚫','🟢','🔵','🟣'];


  const heptadMap = {};

  const suitEmojis = ['♣️','♦️','♥️','♠️'];

  const baseStartDOYs = [

  1,8,15,22,31,38,45,52,61,68,75,82,89,

  92,99,106,113,122,129,136,143,152,159,166,173,180,

  183,190,197,204,213,220,227,234,243,250,257,264,271,

  274,281,288,295,304,311,318,325,334,341,348,355,362

  ];

  let weekNum = 1;

  for(let s=0;s<4;s++){

    const suit = suitEmojis[s];

    for(let w=0; w<13; w++){

      let startDOY = baseStartDOYs[s*13 + w];

      const isLongWeek = (w===3||w===7||w===11);

      const daysInWeek = isLongWeek?9:7;

      for(let d=0;d<daysInWeek;d++){

        const doy=startDOY+d;

        if(doy<=364) heptadMap[doy]=suit+' '+weekNum;

      }

      weekNum++;

    }

    weekNum=1;

  }


  function calculateGPC(){

    const now=new Date();

    const todayUTC=Date.UTC(now.getUTCFullYear(),now.getUTCMonth(),now.getUTCDate());

    let remainingDays=Math.floor((todayUTC-epochDate.getTime())/86400000);

    let gpcYear=epochYear;

    while(true){

      const rel=gpcYear-epochYear;

      let leapDays=0;

      if(rel%7===6) leapDays+=7;

      if(rel%49===48) leapDays+=7;

      if((rel+29)%70===0) leapDays+=7;

      const yearLength=364+leapDays;

      if(remainingDays<yearLength) break;

      remainingDays-=yearLength;

      gpcYear++;

    }

    const doyToday=Math.max(1,Math.min(364,remainingDays+1));

    return {gpcYear,doyToday};

  }


  function updateGPC(){

    const {doyToday}=calculateGPC();

    const emoji=weekEmojis[(doyToday-1)%7]||'';

    let mainText='';

    if(baseMode==='DOY') mainText=String(doyToday).padStart(3,'0');

    else if(baseMode==='DOM'){

      let remaining=doyToday, monthIndex=0;

      for(let i=0;i<gpcMonths.length;i++){

        if(remaining<=gpcMonths[i].days){ monthIndex=i; break; }

        remaining-=gpcMonths[i].days;

      }

      const month=monthIndex+1, day=remaining;

      mainText=String(month).padStart(2,'0') + '.' + String(day).padStart(2,'0');

    }

    else if(baseMode==='DOS'){

      const quarter=Math.floor((doyToday-1)/91)+1;

      const dayInSeason=((doyToday-1)%91)+1;

      mainText='Q'+quarter + ' ' + String(dayInSeason).padStart(2,'0');

    }

    let heptadText = heptadEnabled ? ' ' + (heptadMap[doyToday]||'') : '';

    gpcDisplayEl.textContent = emoji + ' ' + mainText + heptadText;

  }

  updateGPC();

  setInterval(updateGPC,60000);


  gpcBtns.forEach(btn=>{

    btn.addEventListener("click",()=>{

      const mode=btn.dataset.mode;

      if(mode==='HEPTAD'){

        heptadEnabled=!heptadEnabled;

        btn.classList.toggle('active',heptadEnabled);

      } else {

        baseMode=mode;

        gpcBtns.forEach(b=>{

          if(b.dataset.mode!=='HEPTAD') b.classList.toggle('active',b===btn);

        });

      }

      updateGPC();

    });

  });


});

// ===== PERFECT HOMEPROOF SIDEBAR (FIXED) =====

document.addEventListener('DOMContentLoaded', () => {

  const sidebar = document.getElementById('sidebar');

  const sidebarToggle = document.getElementById('sidebar-toggle');

  const HOME_NOTE = "Home";

  

  if (!sidebar || !sidebarToggle) {

    console.error("❌ Sidebar elements missing");

    return;

  }

  

  const recentBtn = document.getElementById('recent-btn') || (() => {

    const btn = document.createElement('div');

    btn.id = 'recent-btn';

    btn.className = 'sidebar-item';

    btn.textContent = '🕘 Recent';

    sidebar.appendChild(btn);

    return btn;

  })();

  

  const recentList = document.getElementById('recent-list') || (() => {

    const list = document.createElement('div');

    list.id = 'recent-list';

    list.style.marginTop = '8px';

    sidebar.appendChild(list);

    return list;

  })();

  

  let recentVisible = false;

  let lastRequestedNote = null;

  let noteCache = {};

  

  // ===== VaultDB tombstone delete helper =====

  async function vaultDelete(noteName) {

    // Set to null to "delete" in your VaultDB

    return VaultDB.set(noteName, null);

  }

  

  // ===== Sidebar toggle =====

  sidebarToggle.addEventListener('click', () => {

    sidebar.classList.toggle('open');

  });

  

  // ===== Recent toggle =====

  recentBtn.addEventListener('click', async (e) => {

    e.stopPropagation();

    recentVisible = !recentVisible;

    recentBtn.textContent = recentVisible ? '🗒️ Hide' : '🗒️ Notes';

    recentList.innerHTML = recentVisible ? '⏳ Loading...' : '';

    if (recentVisible) await loadRecent();

  });

  

  // ===== Load recent notes =====

  async function loadRecent() {

    try {

      const notes = await VaultDB.list();

      if (!notes?.length) {

        recentList.innerHTML = 'No notes yet';

        return;

      }

      

      const notesWithTime = [];

      for (const name of notes) {

        const data = await VaultDB.get(name);

        if (!data) continue; // skip deleted notes

        notesWithTime.push({ name, timestamp: data.timestamp || 0 });

      }

      

      recentList.innerHTML = notesWithTime

        .sort((a, b) => b.timestamp - a.timestamp)

        .slice(0, 10)

        .map(item => `

          <div class="note-item" data-note="${item.name}">

            <span class="note-title">${item.name}</span>

            ${item.name !== HOME_NOTE ? `<span class="delete-note" title="Delete">🗑</span>` : ''}

          </div>

        `)

        .join('');

      

    } catch (err) {

      console.error(err);

      recentList.innerHTML = '❌ Error loading';

    }

  }

  

  // ===== Delegated click handling (OPEN vs DELETE) =====

  recentList.addEventListener('click', (e) => {

    const deleteBtn = e.target.closest('.delete-note');

    const item = e.target.closest('.note-item');

    if (!item) return;

    

    const noteName = item.dataset.note;

    

    if (deleteBtn) {

      e.stopPropagation();

      deleteNote(noteName);

    } else {

      switchToNote(noteName);

    }

  });

  

  // ===== Delete note (Home-safe, cache-safe) =====

  async function deleteNote(noteName) {

    if (!noteName || noteName === HOME_NOTE) return;

    if (!confirm(`Delete "${noteName}" permanently?`)) return;

    

    try {

      await vaultDelete(noteName); // ✅ now exists

      delete noteCache[noteName];

      

      if (currentNote === noteName) {

        currentNote = null;

        editor.value = '';

        await switchToNote(HOME_NOTE);

      }

      

      await loadRecent();

      console.log(`🗑 Deleted "${noteName}"`);

    } catch (err) {

      console.error("❌ Delete failed", err);

      alert("Delete failed — see console");

    }

  }

  

  // ===== Home-proof note switching =====

  async function switchToNote(noteName) {

    noteName = noteName?.trim();

    if (!noteName || noteName === currentNote) return;

    

    lastRequestedNote = noteName;

    

    if (

      currentNote &&

      currentNote !== HOME_NOTE &&

      noteCache[currentNote] !== undefined

    ) {

      await VaultDB.set(currentNote, noteCache[currentNote]);

    }

    

    let content = noteCache[noteName];

    if (content === undefined || content === null) {

      content = await VaultDB.get(noteName);

      if (!content) content = `# ${noteName}\n\n`;

      noteCache[noteName] = content;

    }

    

    if (lastRequestedNote !== noteName) return;

    

    currentNote = noteName;

    editor.value = content;

    

    suppressHistory = true;

    editor.dispatchEvent(new Event('input', { bubbles: true }));

    suppressHistory = false;

    

    document.querySelectorAll('.note-item.active')

      .forEach(el => el.classList.remove('active'));

    

    document.querySelectorAll(`.note-item[data-note="${currentNote}"]`)

      .forEach(el => el.classList.add('active'));

  }

  

  // ===== Keep cache in sync =====

  editor.addEventListener('input', () => {

    if (currentNote) noteCache[currentNote] = editor.value;

  });

});

const SidebarManager = {

  sidebar: null,

  listEl: null,

  folders: {},


  /* =====================

     INIT

     ===================== */

  async init() {

    this.sidebar = document.getElementById("sidebar");

    this.listEl = document.getElementById("recent-list");


    if (!this.sidebar || !this.listEl) {

      console.error("Sidebar elements missing");

      return;

    }


    await this.load();

    await this.syncNotes();

    this.render();

    this.injectControls();

  },


  /* =====================

     STORAGE

     ===================== */

  async load() {

    const raw = await VaultDB.get("__folders__");

    this.folders = raw ? JSON.parse(raw) : { "__root__": [] };

  },


  async save() {

    await VaultDB.set("__folders__", JSON.stringify(this.folders));

  },


  /* =====================

     SYNC NOTES

     ===================== */

 async syncNotes() {

    const notes = (await VaultDB.list()).filter(n => n !== "__folders__");

    

    const known = new Set(Object.values(this.folders).flat());

    

    for (const n of notes) {

      if (known.has(n)) continue;

      

      const data = await VaultDB.get(n);

      if (!data) continue; // skip deleted notes

      

      this.folders.__root__.push(n);

    }

    

    this.sortAll();

    await this.save();

  },

  

  sortAll() {

    Object.values(this.folders).forEach(arr =>

      arr.sort((a, b) => a.localeCompare(b))

    );

  },


  /* =====================

     UI CONTROLS

     ===================== */

  injectControls() {

    const btn = document.createElement("div");

    btn.className = "sidebar-item";

    btn.textContent = "📁 New Folder";

    btn.onclick = () => this.addFolder();

    this.sidebar.appendChild(btn);

  },


  /* =====================

     FOLDER ACTIONS

     ===================== */

  async addFolder() {

    const name = prompt("Folder name?");

    if (!name || this.folders[name]) return;


    this.folders[name] = [];

    await this.save();

    this.render();

  },


  async renameFolder(oldName) {

    const name = prompt("Rename folder:", oldName);

    if (!name || name === oldName || this.folders[name]) return;


    this.folders[name] = this.folders[oldName];

    delete this.folders[oldName];


    await this.save();

    this.render();

  },


  async deleteFolder(name) {

    if (!confirm(`Delete folder "${name}"?`)) return;


    this.folders.__root__.push(...this.folders[name]);

    delete this.folders[name];


    this.sortAll();

    await this.save();

    this.render();

  },


  /* =====================

     NOTE ACTIONS

     ===================== */

  async openNote(name) {

    if (currentNote === name) return;


    if (currentNote) {

      await VaultDB.set(currentNote, editor.value);

    }


    let content = await VaultDB.get(name);

    if (content == null) {

      content = `# ${name}\n\n`;

      await VaultDB.set(name, content);

    }


    currentNote = name;

    editor.value = content;

    updatePreviewAndSave();

  },


  async deleteNote(name) {

    if (name === "Home") return;

    if (!confirm(`Delete note "${name}"?`)) return;


    Object.values(this.folders).forEach(arr => {

      const i = arr.indexOf(name);

      if (i !== -1) arr.splice(i, 1);

    });


    await VaultDB.set(name, null);

    await this.save();

    this.render();


    if (currentNote === name) {

      await this.openNote("Home");

    }

  },


  async renameNote(oldName) {

    const name = prompt("Rename note:", oldName);

    if (!name || name === oldName) return;


    const content = await VaultDB.get(oldName);

    await VaultDB.set(name, content);

    await VaultDB.set(oldName, null);


    Object.values(this.folders).forEach(arr => {

      const i = arr.indexOf(oldName);

      if (i !== -1) arr[i] = name;

    });


    this.sortAll();

    await this.save();

    this.render();


    if (currentNote === oldName) {

      await this.openNote(name);

    }

  },


  async moveNote(note, folder) {

    Object.values(this.folders).forEach(arr => {

      const i = arr.indexOf(note);

      if (i !== -1) arr.splice(i, 1);

    });


    this.folders[folder].push(note);

    this.sortAll();

    await this.save();

    this.render();

  },


  /* =====================

     RENDER

     ===================== */

  render() {

    this.listEl.innerHTML = "";


    // Root notes first

    this.folders.__root__.forEach(n =>

      this.listEl.appendChild(this.noteEl(n))

    );


    // Folders

    Object.keys(this.folders)

      .filter(f => f !== "__root__")

      .sort((a, b) => a.localeCompare(b))

      .forEach(f => this.listEl.appendChild(this.folderEl(f)));

  },


  noteEl(name) {

  const el = document.createElement("div");

  el.className = "note-item";

  

  // Title on the left

  const title = document.createElement("span");

  title.className = "title";

  title.textContent = name;

  title.onclick = () => this.openNote(name);

  

  // Icons on the right

  const icons = document.createElement("span");

  icons.className = "icons";

  

  const ren = document.createElement("span");

  ren.textContent = "✏️";

  ren.title = "Rename";

  ren.onclick = e => {

    e.stopPropagation();

    this.renameNote(name);

  };

  

  const del = document.createElement("span");

  del.textContent = "🗑";

  del.title = "Delete";

  del.onclick = e => {

    e.stopPropagation();

    this.deleteNote(name);

  };

  

  icons.append(ren, del);

  el.append(title, icons);

  

  return el;

},


  folderEl(name) {

  const wrap = document.createElement("div");

  wrap.className = "folder";

  

  // Folder header: title + icons

  const head = document.createElement("div");

  head.className = "note-item";

  

  const title = document.createElement("span");

  title.className = "title";

  title.textContent = `📁 ${name}`;

  

  const icons = document.createElement("span");

  icons.className = "icons";

  

  // Rename icon

  const ren = document.createElement("span");

  ren.textContent = "✏️";

  ren.title = "Rename Folder";

  ren.onclick = e => {

    e.stopPropagation();

    this.renameFolder(name);

  };

  

  // Delete icon

  const del = document.createElement("span");

  del.textContent = "🗑";

  del.title = "Delete Folder";

  del.onclick = e => {

    e.stopPropagation();

    this.deleteFolder(name);

  };

  

  icons.append(ren, del);

  head.append(title, icons);

  wrap.appendChild(head);

  

  // Add notes inside folder

  this.folders[name].forEach(n => wrap.appendChild(this.noteEl(n)));

  

  return wrap;

}

};

</script>

</body>

</html>

No comments:

Post a Comment