(function () {
"use strict";
// Detect Node (fs) availability (Electron / Node) and provide a browser fallback
let nodeFS = false;
let fsModule = null;
let pathModule = null;
let dataFilePath = null;
const browserDataUrl = "assets/js/data/doxa.json"; // relative to index.html
try {
fsModule = require("fs");
pathModule = require("path");
nodeFS = true;
dataFilePath = pathModule.resolve(__dirname, "data", "doxa.json");
} catch (err) {
nodeFS = false;
}
// In-memory state
window.notes = window.notes || [];
window.currentNoteIndex = window.currentNoteIndex || null;
let modalCurrentIndex = null;
function log() {
if (console && console.log) console.log.apply(console, arguments);
}
// Theme handling: store preference in localStorage and toggle body.dark
const THEME_KEY = "doxa_theme";
let systemMediaQuery = null;
let systemMediaListener = null;
function applyTheme(theme) {
try {
// remove previous system listener if any
if (systemMediaQuery && systemMediaListener) {
try {
if (systemMediaQuery.removeEventListener)
systemMediaQuery.removeEventListener("change", systemMediaListener);
else systemMediaQuery.removeListener(systemMediaListener);
} catch (e) {}
systemMediaQuery = null;
systemMediaListener = null;
}
if (theme === "dark") {
document.body.classList.add("dark");
} else if (theme === "light") {
document.body.classList.remove("dark");
} else {
// system: apply based on current system setting and listen for changes
try {
const mql = window.matchMedia("(prefers-color-scheme: dark)");
if (mql.matches) document.body.classList.add("dark");
else document.body.classList.remove("dark");
systemMediaQuery = mql;
systemMediaListener = function (e) {
if (e.matches) document.body.classList.add("dark");
else document.body.classList.remove("dark");
const btn = document.getElementById("themeToggle");
if (btn) {
const currentSys = e.matches ? "tmavý" : "světlý";
btn.setAttribute(
"aria-label",
`Respektovat systém (aktuálně: ${currentSys})`,
);
}
};
if (mql.addEventListener)
mql.addEventListener("change", systemMediaListener);
else mql.addListener(systemMediaListener);
} catch (e) {
// fallback
document.body.classList.remove("dark");
}
}
} catch (e) {
/* ignore */
}
const btn = document.getElementById("themeToggle");
if (btn) {
let icon = "🌓";
let aria = "Respektovat systém";
if (theme === "dark") {
icon = "🌙";
aria = "Tmavý režim (kliknutím přepnete)";
} else if (theme === "light") {
icon = "☀️";
aria = "Světlý režim (kliknutím přepnete)";
} else {
const cur = document.body.classList.contains("dark")
? "tmavý"
: "světlý";
icon = "🌓";
aria = `Respektovat systém (aktuálně: ${cur})`;
}
btn.textContent = icon;
btn.setAttribute("aria-label", aria);
}
}
function detectSystemTheme() {
try {
return window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
} catch (e) {
return "light";
}
}
function getStoredTheme() {
try {
return localStorage.getItem(THEME_KEY);
} catch (e) {
return null;
}
}
function setStoredTheme(value) {
try {
localStorage.setItem(THEME_KEY, value);
} catch (e) {}
}
function initTheme() {
const stored = getStoredTheme();
const theme = stored !== null ? stored : "system";
applyTheme(theme);
}
function toggleTheme() {
const stored = getStoredTheme() || "system";
const order = ["light", "dark", "system"];
let idx = order.indexOf(stored);
if (idx === -1) idx = 2;
const next = order[(idx + 1) % order.length];
setStoredTheme(next);
applyTheme(next);
}
// end theme handling
// --- Markdown editor helpers (toolbar + preview) ---
let mdEngine = null;
function ensureMarkdown() {
if (mdEngine) return mdEngine;
if (window.markdownit) {
try {
mdEngine = window.markdownit({
html: false,
linkify: true,
typographer: true,
});
} catch (e) {
mdEngine = null;
}
return mdEngine;
}
// Fallback simple markdown renderer (safe, offline)
function escapeHtml(str) {
if (str == null) return "";
return String(str)
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/\"/g, """)
.replace(/'/g, "'");
}
function escapeAttr(str) {
return escapeHtml(str).replace(/\"/g, """);
}
function inlineTokenize(s) {
const tokens = [];
function push(html) {
const id = "%%T" + tokens.length + "%%";
tokens.push(html);
return id;
}
// images
s = s.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (m, alt, url) =>
push(
'',
),
);
// links
s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (m, text, url) =>
push('' + escapeHtml(text) + ""),
);
// inline code
s = s.replace(/`([^`]+)`/g, (m, code) =>
push("
" + escapeHtml(code) + ""),
);
// bold
s = s.replace(/\*\*(.+?)\*\*/g, (m, t) =>
push("" + escapeHtml(t) + ""),
);
// italic
s = s.replace(/\*(.+?)\*/g, (m, t) =>
push("" + escapeHtml(t) + ""),
);
// strike
s = s.replace(/~~(.+?)~~/g, (m, t) =>
push("" + escapeHtml(t) + ""),
);
// escape remaining text
let escaped = escapeHtml(s);
// replace placeholders
tokens.forEach((html, i) => {
escaped = escaped.replace("%%T" + i + "%%", html);
});
return escaped;
}
function simpleMarkdownRender(src) {
if (!src) return "";
const lines = String(src).replace(/\r\n/g, "\n").split("\n");
let i = 0,
out = "";
const n = lines.length;
while (i < n) {
let line = lines[i];
// code block
if (/^```/.test(line)) {
const lang = line.slice(3).trim();
let code = "";
i++;
while (i < n && !/^```/.test(lines[i])) {
code += lines[i] + "\n";
i++;
}
i++; // skip closing
out += "
" + escapeHtml(code) + "\n";
continue;
}
// heading
const h = line.match(/^\s*(#{1,6})\s+(.*)$/);
if (h) {
out +=
"| " + inlineTokenize(hp) + " | ") .join("") + "
|---|
| " + inlineTokenize(c) + " | ") .join("") + "
" + inlineTokenize(quote.trim()) + "\n"; continue; } // paragraph (collect until blank) if (/^\s*$/.test(line)) { i++; continue; } let para = line; i++; while ( i < n && !/^\s*$/.test(lines[i]) && !/^(#{1,6})\s+/.test(lines[i]) && !/^```/.test(lines[i]) && lines[i].indexOf("|") === -1 && !/^\s*[-*]\s+/.test(lines[i]) && !/^\s*\d+\.\s+/.test(lines[i]) && !/^\s*>\s?/.test(lines[i]) ) { para += "\n" + lines[i]; i++; } out += "
" + inlineTokenize(para.trim()) + "
\n"; } return out; } mdEngine = { render: simpleMarkdownRender }; return mdEngine; } function sanitizeHtml(html) { if (window.DOMPurify && window.DOMPurify.sanitize) return window.DOMPurify.sanitize(html); return html; } function renderPreviewFor(textareaEl, previewEl) { if (!textareaEl || !previewEl) return; const md = ensureMarkdown(); if (!md) { previewEl.innerText = "Preview není dostupný (chybí knihovna)."; return; } const raw = textareaEl.value || ""; let html = ""; try { html = md.render(raw); } catch (e) { html = "" + String(raw) + ""; } html = sanitizeHtml(html); previewEl.innerHTML = html; // syntax highlight if available if (window.hljs && window.hljs.highlightElement) { previewEl.querySelectorAll("pre code").forEach((block) => { try { window.hljs.highlightElement(block); } catch (e) {} }); } } function scheduleRender(textareaEl, previewEl, delay = 200) { if (!previewEl) return; try { if (previewEl._renderTimer) clearTimeout(previewEl._renderTimer); } catch (e) {} previewEl._renderTimer = setTimeout(() => { try { renderPreviewFor(textareaEl, previewEl); } catch (e) {} previewEl._renderTimer = null; }, delay); } // Live preview storage key and helpers const LIVE_PREVIEW_KEY = "doxa_livepreview"; function getStoredLive() { try { return localStorage.getItem(LIVE_PREVIEW_KEY) === "1"; } catch (e) { return false; } } function setStoredLive(v) { try { localStorage.setItem(LIVE_PREVIEW_KEY, v ? "1" : "0"); } catch (e) {} } function applyLiveStateGlobally() { const on = getStoredLive(); const mainLiveBtn = document.getElementById("livePreviewToggle"); const mainPreview = document.getElementById("notePreview"); const mainTextarea = document.getElementById("noteInput"); if (mainLiveBtn) { if (on) mainLiveBtn.classList.add("active"); else mainLiveBtn.classList.remove("active"); } if (on && mainPreview && mainTextarea) { mainPreview.classList.remove("hidden"); mainPreview.setAttribute("aria-hidden", "false"); try { scheduleRender(mainTextarea, mainPreview, 0); } catch (e) {} } // modal (if present) const modalLiveBtn = document.getElementById("livePreviewToggleModal"); const modalPreview = document.getElementById("modalNotePreview"); const modalTextarea = document.getElementById("modalNoteContent"); if (modalLiveBtn) { if (on) modalLiveBtn.classList.add("active"); else modalLiveBtn.classList.remove("active"); } if (on && modalPreview && modalTextarea) { modalPreview.classList.remove("hidden"); modalPreview.setAttribute("aria-hidden", "false"); try { scheduleRender(modalTextarea, modalPreview, 0); } catch (e) {} } } const AUTOSAVE_DELAY = 1000; let autosaveTimer = null; function scheduleAutosave() { try { if (autosaveTimer) clearTimeout(autosaveTimer); } catch (e) {} autosaveTimer = setTimeout(() => { try { saveNotesToFile(false); } catch (err) { /* ignore */ } const statusEl = document.getElementById("saveStatus"); if (statusEl) { statusEl.textContent = "Uloženo"; try { if (window._saveStatusTimeout) clearTimeout(window._saveStatusTimeout); } catch (e) {} window._saveStatusTimeout = setTimeout(() => { statusEl.textContent = ""; }, 1500); } }, AUTOSAVE_DELAY); } function wrapSelection(textarea, before, after) { if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const value = textarea.value; const selected = value.slice(start, end) || ""; const newValue = value.slice(0, start) + before + selected + (after !== undefined ? after : before) + value.slice(end); textarea.value = newValue; textarea.focus(); const cursorStart = start + before.length; textarea.selectionStart = cursorStart; textarea.selectionEnd = cursorStart + selected.length; // trigger input event so autosave and model update run try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function insertCodeBlock(textarea) { if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const value = textarea.value; const selected = value.slice(start, end) || ""; if (!selected) { const insert = "```\n\n```"; textarea.value = value.slice(0, start) + insert + value.slice(end); textarea.focus(); const cursor = start + 4; // after ```\n textarea.selectionStart = cursor; textarea.selectionEnd = cursor; try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } else { const newValue = value.slice(0, start) + "```\n" + selected + "\n```" + value.slice(end); textarea.value = newValue; textarea.focus(); textarea.selectionStart = start + 4; textarea.selectionEnd = start + 4 + selected.length; try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } } function insertLink(textarea) { if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const value = textarea.value; const selected = value.slice(start, end) || "odkaz"; const url = prompt("Zadejte URL (např. https://example.com):", "https://"); if (!url) return; const newValue = value.slice(0, start) + "[" + selected + "](" + url + ")" + value.slice(end); textarea.value = newValue; textarea.focus(); const cursorStart = start + 1; textarea.selectionStart = cursorStart; textarea.selectionEnd = cursorStart + selected.length; try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function getLineInfo(textarea) { const value = textarea.value; const start = textarea.selectionStart; const end = textarea.selectionEnd; const lineStart = value.lastIndexOf("\n", Math.max(0, start - 1)) + 1; let nextNewline = value.indexOf("\n", end); if (nextNewline === -1) nextNewline = value.length; const segment = value.slice(lineStart, nextNewline); const lines = segment.split("\n"); return { value, start, end, lineStart, lineEnd: nextNewline, lines }; } function insertHeading(textarea, level) { if (!textarea) return; const info = getLineInfo(textarea); const lines = info.lines; const prefix = "#".repeat(level) + " "; const specificRegex = new RegExp("^\\s*" + "#".repeat(level) + "\\s+"); const anyHeadingRegex = /^\s*#{1,6}\s+/; const allHaveSpecific = lines.every((l) => specificRegex.test(l)); let newLines; if (allHaveSpecific) newLines = lines.map((l) => l.replace(specificRegex, "")); else newLines = lines.map((l) => prefix + l.replace(anyHeadingRegex, "")); const newSegment = newLines.join("\n"); textarea.value = info.value.slice(0, info.lineStart) + newSegment + info.value.slice(info.lineEnd); textarea.selectionStart = info.lineStart; textarea.selectionEnd = info.lineStart + newSegment.length; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function toggleUnorderedList(textarea) { if (!textarea) return; const info = getLineInfo(textarea); const lines = info.lines; const allHave = lines.every((l) => /^\s*([-*])\s+/.test(l)); let newLines; if (allHave) newLines = lines.map((l) => l.replace(/^\s*([-*])\s+/, "")); else newLines = lines.map((l) => l.trim() === "" ? "- " : "- " + l.replace(/^\s*/, ""), ); const newSegment = newLines.join("\n"); textarea.value = info.value.slice(0, info.lineStart) + newSegment + info.value.slice(info.lineEnd); textarea.selectionStart = info.lineStart; textarea.selectionEnd = info.lineStart + newSegment.length; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function toggleOrderedList(textarea) { if (!textarea) return; const info = getLineInfo(textarea); const lines = info.lines; const allHave = lines.every((l) => /^\s*\d+\.\s+/.test(l)); let newLines; if (allHave) newLines = lines.map((l) => l.replace(/^\s*\d+\.\s+/, "")); else newLines = lines.map((l, i) => l.trim() === "" ? i + 1 + ". " : i + 1 + ". " + l.replace(/^\s*/, ""), ); const newSegment = newLines.join("\n"); textarea.value = info.value.slice(0, info.lineStart) + newSegment + info.value.slice(info.lineEnd); textarea.selectionStart = info.lineStart; textarea.selectionEnd = info.lineStart + newSegment.length; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function toggleBlockquote(textarea) { if (!textarea) return; const info = getLineInfo(textarea); const lines = info.lines; const allHave = lines.every((l) => /^\s*>\s?/.test(l)); let newLines; if (allHave) newLines = lines.map((l) => l.replace(/^\s*>\s?/, "")); else newLines = lines.map((l) => l.trim() === "" ? "> " : "> " + l.replace(/^\s*/, ""), ); const newSegment = newLines.join("\n"); textarea.value = info.value.slice(0, info.lineStart) + newSegment + info.value.slice(info.lineEnd); textarea.selectionStart = info.lineStart; textarea.selectionEnd = info.lineStart + newSegment.length; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function insertHr(textarea) { if (!textarea) return; const pos = textarea.selectionStart; const value = textarea.value; const insert = "\n---\n"; textarea.value = value.slice(0, pos) + insert + value.slice(pos); textarea.selectionStart = pos + insert.length; textarea.selectionEnd = textarea.selectionStart; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function insertImage(textarea) { if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const value = textarea.value; const selected = value.slice(start, end) || ""; const url = prompt("Adresa obrázku (URL):", "https://"); if (!url) return; const alt = selected || prompt("Alt text (krátký popis):", ""); const insert = ""; textarea.value = value.slice(0, start) + insert + value.slice(end); textarea.selectionStart = start + insert.length; textarea.selectionEnd = textarea.selectionStart; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function toggleTaskList(textarea) { if (!textarea) return; const info = getLineInfo(textarea); const lines = info.lines; const allHave = lines.every((l) => /^\s*-\s*\[[ xX]\]\s+/.test(l)); let newLines; if (allHave) newLines = lines.map((l) => l.replace(/^\s*-\s*\[[ xX]\]\s+/, "")); else newLines = lines.map((l) => l.trim() === "" ? "- [ ] " : "- [ ] " + l.replace(/^\s*/, ""), ); const newSegment = newLines.join("\n"); textarea.value = info.value.slice(0, info.lineStart) + newSegment + info.value.slice(info.lineEnd); textarea.selectionStart = info.lineStart; textarea.selectionEnd = info.lineStart + newSegment.length; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function insertTable(textarea) { if (!textarea) return; const colsStr = prompt("Počet sloupců (např. 2):", "2"); if (!colsStr) return; const rowsStr = prompt("Počet řádků (bez hlavičky, např. 2):", "2"); if (!rowsStr) return; const cols = Math.max(1, parseInt(colsStr, 10) || 2); const rows = Math.max(0, parseInt(rowsStr, 10) || 2); // build header const headers = []; const aligns = []; for (let c = 1; c <= cols; c++) { headers.push("Header " + c); aligns.push("---"); } const headerLine = "| " + headers.join(" | ") + " |\n"; const alignLine = "| " + aligns.join(" | ") + " |\n"; let body = ""; for (let r = 0; r < rows; r++) { const cells = []; for (let c = 0; c < cols; c++) cells.push(""); body += "| " + cells.join(" | ") + " |\n"; } const insert = headerLine + alignLine + body; const pos = textarea.selectionStart || textarea.value.length; textarea.value = textarea.value.slice(0, pos) + insert + textarea.value.slice(pos); textarea.selectionStart = pos + insert.length; textarea.selectionEnd = textarea.selectionStart; textarea.focus(); try { textarea.dispatchEvent(new Event("input", { bubbles: true })); } catch (e) { try { textarea.dispatchEvent(new Event("input")); } catch (e) {} } } function togglePreview(textareaId, previewId) { const textarea = document.getElementById(textareaId); const preview = document.getElementById(previewId); if (!preview) return; const isHidden = preview.classList.contains("hidden"); if (isHidden) { // show + render scheduleRender(textarea, preview, 0); preview.classList.remove("hidden"); preview.setAttribute("aria-hidden", "false"); } else { preview.classList.add("hidden"); preview.setAttribute("aria-hidden", "true"); } } function attachToolbar(toolbarEl, textareaEl, previewEl) { if (!toolbarEl || !textareaEl) return; toolbarEl.querySelectorAll("button[data-action]").forEach((btn) => { btn.addEventListener("click", (e) => { const action = btn.getAttribute("data-action"); e.preventDefault(); switch (action) { case "bold": wrapSelection(textareaEl, "**", "**"); break; case "italic": wrapSelection(textareaEl, "*", "*"); break; case "code": wrapSelection(textareaEl, "`", "`"); break; case "codeblock": insertCodeBlock(textareaEl); break; case "link": insertLink(textareaEl); break; case "h1": insertHeading(textareaEl, 1); break; case "h2": insertHeading(textareaEl, 2); break; case "h3": insertHeading(textareaEl, 3); break; case "ul": toggleUnorderedList(textareaEl); break; case "ol": toggleOrderedList(textareaEl); break; case "quote": toggleBlockquote(textareaEl); break; case "hr": insertHr(textareaEl); break; case "image": insertImage(textareaEl); break; case "strike": wrapSelection(textareaEl, "~~", "~~"); break; case "task": toggleTaskList(textareaEl); break; case "table": insertTable(textareaEl); break; } }); }); // preview toggle button (if present) const previewBtn = toolbarEl.querySelector("#togglePreviewMain") || toolbarEl.querySelector("#togglePreviewModal"); if (previewBtn) { previewBtn.addEventListener("click", (e) => { e.preventDefault(); togglePreview(textareaEl.id, previewEl.id); }); } } // --- end markdown editor helpers --- function loadNotes() { if (nodeFS) { try { // Ensure file exists if (!fsModule.existsSync(dataFilePath)) { fsModule.mkdirSync(pathModule.dirname(dataFilePath), { recursive: true, }); fsModule.writeFileSync( dataFilePath, JSON.stringify({ notes: [] }, null, 2), "utf8", ); } const raw = fsModule.readFileSync(dataFilePath, "utf8"); const data = raw ? JSON.parse(raw) : {}; window.notes = data.notes || []; if (!window.notes || window.notes.length === 0) window.currentNoteIndex = null; } catch (err) { console.error("Chyba při čtení datového souboru:", err); window.notes = window.notes || []; } updateNotesList(); if (window.currentNoteIndex != null) switchNotes(window.currentNoteIndex); return; } // Browser fallback: try localStorage first, then static JSON file try { const saved = localStorage.getItem("doxa_notes_v1"); if (saved) { const parsed = JSON.parse(saved); window.notes = parsed.notes || []; updateNotesList(); if (window.currentNoteIndex != null) switchNotes(window.currentNoteIndex); return; } } catch (err) { console.warn("Chyba při načítání z localStorage:", err); } fetch(browserDataUrl) .then((resp) => { if (!resp.ok) { window.notes = []; updateNotesList(); return; } return resp .json() .then((data) => { window.notes = data.notes || []; updateNotesList(); }) .catch((err) => { console.error("Chyba při parsování JSON ze souboru:", err); window.notes = []; updateNotesList(); }); }) .catch((err) => { console.warn("Nelze načíst statický soubor:", err); window.notes = []; updateNotesList(); }); } function saveNotesToFile(triggerDownload = true) { const payload = { notes: window.notes || [] }; const json = JSON.stringify(payload, null, 2); if (nodeFS) { try { const dir = pathModule.dirname(dataFilePath); if (!fsModule.existsSync(dir)) { fsModule.mkdirSync(dir, { recursive: true }); } fsModule.writeFileSync(dataFilePath, json, "utf8"); log("Data uložena do", dataFilePath); if (triggerDownload) alert("Poznámky byly uloženy."); } catch (err) { alert("Chyba při ukládání souboru: " + (err.message || err)); } return; } // Browser fallback: save to localStorage try { localStorage.setItem("doxa_notes_v1", json); } catch (err) { console.warn("Nepodařilo se uložit do localStorage:", err); } if (triggerDownload) { const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(json); const downloadAnchor = document.createElement("a"); downloadAnchor.setAttribute("href", dataStr); downloadAnchor.setAttribute("download", "doxa.json"); document.body.appendChild(downloadAnchor); downloadAnchor.click(); downloadAnchor.remove(); } } function reorderNotes(fromIndex, toIndex) { if (fromIndex === toIndex) return; if (!Array.isArray(window.notes)) return; // Keep track of selected note id to restore selection after reorder const selectedId = window.notes[window.currentNoteIndex] ? window.notes[window.currentNoteIndex].id : null; // Remove item from array const item = window.notes.splice(fromIndex, 1)[0]; // Adjust insert position because we removed an earlier element let insertAt = toIndex; if (toIndex > fromIndex) insertAt = toIndex - 1; if (insertAt < 0) insertAt = 0; if (insertAt > window.notes.length) insertAt = window.notes.length; window.notes.splice(insertAt, 0, item); // restore selection by id (robust across reorders) if (selectedId != null) { const newIndex = window.notes.findIndex((n) => n.id === selectedId); window.currentNoteIndex = newIndex === -1 ? null : newIndex; } saveNotesToFile(false); updateNotesList(); } function moveNoteUp(index) { if (index <= 0) return; reorderNotes(index, index - 1); } function moveNoteDown(index) { if (index < 0 || index >= window.notes.length - 1) return; // toIndex = index + 2 will place after the next element (because of removal adjustment) reorderNotes(index, index + 2); } function deleteNote(index) { if (!window.notes || index < 0 || index >= window.notes.length) return; const note = window.notes[index]; if (!confirm(`Smazat poznámku "${note.name}"?`)) return; const selectedId = window.notes[window.currentNoteIndex] ? window.notes[window.currentNoteIndex].id : null; window.notes.splice(index, 1); // If selected note deleted, try to select neighbor if (selectedId != null && selectedId === note.id) { if (window.notes.length === 0) { window.currentNoteIndex = null; document.getElementById("noteTitle").value = ""; document.getElementById("noteInput").value = ""; } else { const newIndex = Math.min(index, window.notes.length - 1); window.currentNoteIndex = newIndex; switchNotes(window.currentNoteIndex); } } else if ( window.currentNoteIndex != null && window.currentNoteIndex > index ) { // adjust index of current selection window.currentNoteIndex -= 1; } saveNotesToFile(false); updateNotesList(); } function ensureModal() { if (document.getElementById("editModalOverlay")) return; const overlay = document.createElement("div"); overlay.id = "editModalOverlay"; overlay.className = "modal-overlay hidden"; overlay.innerHTML = `