diff --git a/shaarli-pro/css/custom_views.css b/shaarli-pro/css/custom_views.css index c50c168..31b9f6d 100644 --- a/shaarli-pro/css/custom_views.css +++ b/shaarli-pro/css/custom_views.css @@ -265,7 +265,7 @@ body.view-notes .content-container { align-items: center; padding: 12px 16px; cursor: text; - color: var(--text-light, #80868b); + color: var(--note-card-fg, var(--text-light, #80868b)); font-weight: 500; font-size: 1rem; } @@ -282,6 +282,77 @@ body.view-notes .content-container { max-height: 72vh; } +.note-input-expanded-actions { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-top: 2px; + flex-wrap: wrap; +} + +.note-input-expanded .note-formatting-bar, +.note-modal .note-formatting-bar { + width: 100%; + border-radius: 10px; + padding: 6px 8px; +} + +.todo-draft-expanded { + gap: 8px; +} + +.todo-draft-list { + display: flex; + flex-direction: column; + gap: 6px; + max-height: 42vh; + overflow: auto; + padding-right: 2px; +} + +.todo-draft-row { + display: grid; + grid-template-columns: 26px minmax(0, 1fr) 32px; + gap: 8px; + align-items: center; + padding: 2px 0; + border-radius: 8px; +} + +.todo-draft-row.is-checked { + opacity: 0.75; +} + +.todo-draft-row .todo-item-text { + padding: 5px 4px; + font-size: 0.95rem; +} + +.todo-draft-add-btn { + border: none; + background: none; + color: inherit; + opacity: 0.8; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 8px; + width: fit-content; + border-radius: 8px; + padding: 6px 8px; + margin-left: -2px; +} + +.todo-draft-add-btn:hover { + opacity: 1; + background: rgba(0, 0, 0, 0.08); +} + +[data-theme="dark"] .todo-draft-add-btn:hover { + background: rgba(255, 255, 255, 0.14); +} + .note-input-title { width: 100%; border: 0; @@ -461,16 +532,23 @@ body.view-notes .content-container { gap: 16px; } +.note-input-type-actions { + gap: 6px; +} + .note-input-actions button { background: none; border: none; - cursor: pointer; - color: var(--text-light, #80868b); - padding: 4px; + color: inherit; + opacity: 0.9; + width: 32px; + height: 32px; border-radius: 50%; - display: flex; + display: inline-flex; align-items: center; justify-content: center; + cursor: pointer; + padding: 0; transition: background-color 0.2s, color 0.2s; } @@ -479,6 +557,10 @@ body.view-notes .content-container { color: var(--text-color); } +[data-theme="dark"] .note-input-actions button:hover { + color: #e8eaed; +} + /* Tools (View Toggle) */ .notes-tools { position: absolute; @@ -509,9 +591,7 @@ body.view-notes .content-container { .icon-btn.active { color: var(--primary-color, #202124); - /* or specific active color */ background-color: rgba(136, 136, 136, 0.1); - /* Keep highlight style */ } [data-theme="dark"] .icon-btn { @@ -523,14 +603,33 @@ body.view-notes .content-container { } /* --- LOGIC: Masonry vs List --- */ - -/* Masonry Grid */ .notes-masonry { column-count: 4; column-gap: 16px; width: 100%; } +.notes-masonry .note-card { + display: inline-block; + width: 100%; + margin: 0 0 16px; + break-inside: avoid; + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + column-span: none; + border-radius: 12px; + overflow: hidden; +} + +.note-card { + border-radius: 12px; + overflow: hidden; +} + +.note-card .note-inner { + padding: 14px 16px 12px; +} + @media (max-width: 1200px) { .notes-masonry { column-count: 3; @@ -549,7 +648,6 @@ body.view-notes .content-container { } } -/* List View */ .notes-list-view { display: flex; flex-direction: column; @@ -631,7 +729,16 @@ body.note-modal-open { .note-modal-content { flex: 1; overflow-y: auto; - padding: 0; + padding: 6px 20px 10px; +} + +.note-modal-description-preview { + width: 100%; + min-height: 200px; + max-height: 58vh; + overflow: auto; + cursor: text; + padding: 2px 0; } .note-modal-description-source { @@ -639,7 +746,7 @@ body.note-modal-open { border: none; background: transparent; color: inherit; - padding: 0; + padding: 2px 0; resize: none; min-height: 200px; max-height: 58vh; @@ -662,6 +769,22 @@ body.note-modal-open { display: none; } +.note-modal-overlay .note-modal-description-preview { + display: block; +} + +.note-modal-overlay .note-modal-description-source { + display: none; +} + +.note-modal-overlay.note-modal-editing .note-modal-description-preview { + display: none; +} + +.note-modal-overlay.note-modal-editing .note-modal-description-source { + display: block; +} + .note-modal .note-body * { color: inherit; } @@ -674,270 +797,326 @@ body.note-modal-open { flex-shrink: 0; } - .note-modal-tags.is-empty { - display: none; - } +.note-modal-tags.is-empty { + display: none; +} - .note-modal-tags .note-tag { - display: inline-flex; - align-items: center; - gap: 0.25rem; - background: var(--tag-bg); - color: var(--tag-text); - border-radius: 999px; - padding: 0.25rem 0.5rem; - font-size: 0.75rem; - font-weight: 500; - } +.note-modal-tags .note-tag { + display: inline-flex; + align-items: center; + gap: 0.25rem; + background: var(--tag-bg); + color: var(--tag-text); + border-radius: 999px; + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + font-weight: 500; +} - .note-modal-tags .note-tag-text { - line-height: 1.2; - } +.note-modal-tags .note-tag-text { + line-height: 1.2; +} - .note-modal-tags .note-tag-remove-btn { - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.1rem; - height: 1.1rem; - border: none; - border-radius: 999px; - background: rgba(0, 0, 0, 0.12); - color: currentColor; - cursor: pointer; - padding: 0; - line-height: 1; - font-size: 0.8rem; - } +.note-modal-tags .note-tag-remove-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.1rem; + height: 1.1rem; + border: none; + border-radius: 999px; + background: rgba(0, 0, 0, 0.12); + color: currentColor; + cursor: pointer; + padding: 0; + line-height: 1; + font-size: 0.8rem; +} - [data-theme="dark"] .note-modal-tags .note-tag-remove-btn { - background: rgba(255, 255, 255, 0.18); - } +[data-theme="dark"] .note-modal-tags .note-tag-remove-btn { + background: rgba(255, 255, 255, 0.18); +} - .note-modal-actions { - display: flex; - align-items: center; - justify-content: space-between; - gap: 6px; - padding: 8px 16px 16px; - background: transparent; - flex-shrink: 0; - flex-wrap: wrap; - } +.note-modal-actions { + display: flex; + align-items: center; + justify-content: space-between; + gap: 6px; + padding: 8px 16px 16px; + background: transparent; + flex-shrink: 0; + flex-wrap: wrap; +} - [data-theme="dark"] .note-modal-actions { - background: transparent; - } +[data-theme="dark"] .note-modal-actions { + background: transparent; +} - .note-modal-actions-left { - display: flex; - align-items: center; - gap: 2px; - flex-wrap: wrap; - } +.note-modal-actions-left { + display: flex; + align-items: center; + gap: 2px; + flex-wrap: wrap; +} - .note-modal-actions-left > button, - .note-modal-actions-left > a, - .note-modal-actions-left > .note-modal-color-picker > button { - background: transparent; - border: none; - color: inherit; - opacity: 0.9; - width: 32px; - height: 32px; - border-radius: 50%; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - text-decoration: none; - } +.note-modal-actions-left > button, +.note-modal-actions-left > a, +.note-modal-actions-left > .note-modal-color-picker > button { + background: transparent; + border: none; + color: inherit; + opacity: 0.9; + width: 32px; + height: 32px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + text-decoration: none; +} - .note-modal-actions-left > button:hover, - .note-modal-actions-left > a:hover, - .note-modal-actions-left > .note-modal-color-picker > button:hover { - opacity: 1; - background: rgba(0, 0, 0, 0.08); - } +.note-modal-actions-left > button:hover, +.note-modal-actions-left > a:hover, +.note-modal-actions-left > .note-modal-color-picker > button:hover { + opacity: 1; + background: rgba(0, 0, 0, 0.08); +} - [data-theme="dark"] .note-modal-actions-left > button:hover, - [data-theme="dark"] .note-modal-actions-left > a:hover, - [data-theme="dark"] .note-modal-actions-left > .note-modal-color-picker > button:hover { - background: rgba(255, 255, 255, 0.14); - } +[data-theme="dark"] .note-modal-actions-left > button:hover, +[data-theme="dark"] .note-modal-actions-left > a:hover, +[data-theme="dark"] .note-modal-actions-left > .note-modal-color-picker > button:hover { + background: rgba(255, 255, 255, 0.14); +} - .note-modal-close-btn { - background: transparent; - border: none; - color: inherit; - font-weight: 600; - border-radius: 8px; - padding: 6px 10px; - cursor: pointer; - } +.note-modal-close-btn { + background: transparent; + border: none; + color: inherit; + font-weight: 600; + border-radius: 8px; + padding: 6px 10px; + cursor: pointer; +} - .note-modal-close-btn:hover { - background: rgba(0, 0, 0, 0.08); - } +.note-modal-close-btn:hover { + background: rgba(0, 0, 0, 0.08); +} - [data-theme="dark"] .note-modal-close-btn:hover { - background: rgba(255, 255, 255, 0.14); - } +[data-theme="dark"] .note-modal-close-btn:hover { + background: rgba(255, 255, 255, 0.14); +} - .note-modal-pin-toggle { - background: transparent; - border: none; - color: inherit; - opacity: 0.85; - width: 34px; - height: 34px; - border-radius: 50%; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - flex-shrink: 0; - } +.note-modal-pin-toggle { + background: transparent; + border: none; + color: inherit; + opacity: 0.85; + width: 34px; + height: 34px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + flex-shrink: 0; +} - .note-modal-pin-toggle:hover, - .note-modal-pin-toggle.active { - opacity: 1; - background-color: rgba(0, 0, 0, 0.08); - } +.note-modal-pin-toggle:hover, +.note-modal-pin-toggle.active { + opacity: 1; + background-color: rgba(0, 0, 0, 0.08); +} - [data-theme="dark"] .note-modal-pin-toggle:hover, - [data-theme="dark"] .note-modal-pin-toggle.active { - background-color: rgba(255, 255, 255, 0.14); - } +[data-theme="dark"] .note-modal-pin-toggle:hover, +[data-theme="dark"] .note-modal-pin-toggle.active { + background-color: rgba(255, 255, 255, 0.14); +} - /* --- CARD STYLING --- */ - .note-card { - background-color: var(--background-secondary, #ffffff); - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 8px; - margin-bottom: 16px; - break-inside: avoid; - position: relative; - transition: box-shadow 0.2s cubic-bezier(0.4, 0.0, 0.2, 1), transform 0.2s cubic-bezier(0.4, 0.0, 0.2, 1), border-color 0.2s cubic-bezier(0.4, 0.0, 0.2, 1); - overflow: visible; - color: var(--note-card-fg, #202124); - } +.note-body { + font-size: 0.875rem; + line-height: 1.4; + color: var(--text-color, #202124); + word-wrap: break-word; + display: block; + overflow: hidden; + max-height: 360px; +} - [data-theme="dark"] .note-card { - background-color: #202124; - border-color: #5f6368; - color: #e8eaed; - } +[data-theme="dark"] .note-body { + color: #e8eaed; +} - .note-card:hover { - box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15); - border-color: transparent; - } +.note-body p, +.note-modal-content p { + margin: 0 0 0.5rem; +} - [data-theme="dark"] .note-card:hover { - background-color: #202124; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.6), 0 1px 3px 1px rgba(0, 0, 0, 0.4); - border-color: transparent; - } +.note-body ul, +.note-body ol, +.note-modal-content ul, +.note-modal-content ol { + margin: 0 0 0.5rem 1.2rem; + padding: 0; +} - /* Cover Image */ - .note-cover { - overflow: hidden; - border-radius: 8px 8px 0 0; - } +.note-body li, +.note-modal-content li { + margin: 0 0 0.3rem; +} - .note-cover img { - width: 100%; - height: auto; - display: block; - object-fit: cover; - } +.note-body blockquote, +.note-modal-content blockquote { + margin: 0.2rem 0 0.6rem; + padding: 0.2rem 0.75rem; + border-left: 3px solid rgba(0, 0, 0, 0.22); + opacity: 0.9; +} - /* Inner Content */ - .note-inner { - padding: 12px 16px; - display: flex; - flex-direction: column; - gap: 8px; - position: relative; - z-index: 2; - } +[data-theme="dark"] .note-body blockquote, +[data-theme="dark"] .note-modal-content blockquote { + border-left-color: rgba(255, 255, 255, 0.28); +} - /* Title */ - .note-title { - font-size: 1rem; - font-weight: 500; - margin: 0; - line-height: 1.5rem; - color: var(--text-color, #202124); - } +.note-body code, +.note-modal-content code { + font-family: Consolas, "Courier New", monospace; + background: rgba(0, 0, 0, 0.08); + border-radius: 4px; + padding: 0.05rem 0.35rem; + font-size: 0.86em; +} - [data-theme="dark"] .note-title { - color: #e8eaed; - } +[data-theme="dark"] .note-body code, +[data-theme="dark"] .note-modal-content code { + background: rgba(255, 255, 255, 0.12); +} - /* Body (Truncated) */ - .note-body { - font-size: 0.875rem; - line-height: 1.25rem; - color: var(--text-color, #202124); - word-wrap: break-word; - display: -webkit-box; - -webkit-line-clamp: 12; - line-clamp: 12; - -webkit-box-orient: vertical; - overflow: hidden; - max-height: 300px; - } +.md-code-block { + margin: 0.4rem 0 0.7rem; + background: rgba(0, 0, 0, 0.08); + border-radius: 8px; + overflow: auto; +} - [data-theme="dark"] .note-body { - color: #e8eaed; - } +.md-code-block code { + display: block; + padding: 0.65rem 0.75rem; + background: none; + border-radius: 0; +} - /* Tags */ - .note-tags { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-top: 4px; - } +[data-theme="dark"] .md-code-block { + background: rgba(255, 255, 255, 0.12); +} - .note-tag { - display: inline-flex; - align-items: center; - gap: 0.25rem; - background: var(--tag-bg, #f1f3f4); - padding: 0.25rem 0.5rem; - border-radius: 999px; - font-size: 0.75rem; - font-weight: 500; - color: var(--tag-text, #3c4043); - } +.md-spacer { + height: 0.35rem; +} - [data-theme="dark"] .note-tag { - background: var(--tag-bg, #3c4043); - color: var(--tag-text, #e8eaed); - } +.md-todo-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.3rem; +} - .note-tag-text { - line-height: 1.2; - } +.md-todo-item { + display: flex; + align-items: center; + gap: 0.55rem; +} - .note-tag-remove-btn { - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.1rem; - height: 1.1rem; - border: none; - border-radius: 999px; - background: rgba(0, 0, 0, 0.12); - color: currentColor; - cursor: pointer; - padding: 0; - line-height: 1; - font-size: 0.8rem; - } +.md-todo-item.is-checked { + opacity: 0.72; +} + +.md-todo-item.is-checked .md-todo-text { + text-decoration: line-through; +} + +.md-todo-box { + width: 15px; + height: 15px; + border: 2px solid currentColor; + border-radius: 4px; + display: inline-flex; + align-items: center; + justify-content: center; + opacity: 0.7; + flex: 0 0 auto; +} + +.md-todo-box i { + font-size: 13px; + opacity: 0; +} + +.md-todo-item.is-checked .md-todo-box i { + opacity: 1; +} + +@media (max-width: 900px) { + .notes-top-bar { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .notes-tools { + position: static; + transform: none; + justify-content: flex-end; + width: 100%; + } +} + +/* Tags */ +.note-tags { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 4px; +} + +.note-tag { + display: inline-flex; + align-items: center; + gap: 0.25rem; + background: var(--tag-bg, #f1f3f4); + padding: 0.25rem 0.5rem; + border-radius: 999px; + font-size: 0.75rem; + font-weight: 500; + color: var(--tag-text, #3c4043); +} + +[data-theme="dark"] .note-tag { + background: var(--tag-bg, #3c4043); + color: var(--tag-text, #e8eaed); +} + +.note-tag-text { + line-height: 1.2; +} + +.note-tag-remove-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.1rem; + height: 1.1rem; + border: none; + border-radius: 999px; + background: rgba(0, 0, 0, 0.12); + color: currentColor; + cursor: pointer; + padding: 0; + line-height: 1; + font-size: 0.8rem; +} [data-theme="dark"] .note-tag-remove-btn { background: rgba(255, 255, 255, 0.18); @@ -960,7 +1139,7 @@ body.note-modal-open { gap: 2px; margin-top: 12px; margin-left: -6px; - color: var(--text-light, #5f6368); + color: var(--note-card-fg, currentColor); opacity: 0; transition: opacity 0.2s cubic-bezier(0.4, 0.0, 0.2, 1); position: relative; @@ -969,7 +1148,7 @@ body.note-modal-open { } [data-theme="dark"] .note-hover-actions { - color: #9aa0a6; + color: var(--note-card-fg, currentColor); } /* Show actions on hover */ @@ -1007,7 +1186,7 @@ body.note-modal-open { .note-hover-actions > a:hover, .note-hover-actions > div > button:hover { background-color: rgba(0, 0, 0, 0.08); - color: var(--text-color, #202124); + color: inherit; opacity: 1; } @@ -1015,7 +1194,7 @@ body.note-modal-open { [data-theme="dark"] .note-hover-actions > a:hover, [data-theme="dark"] .note-hover-actions > div > button:hover { background-color: rgba(255, 255, 255, 0.14); - color: #e8eaed; + color: inherit; } .bookmark-palette { @@ -2430,8 +2609,9 @@ body.view-todo .note-card.todo-card .todo-checklist-preview-wrap { -webkit-line-clamp: initial; line-clamp: initial; -webkit-box-orient: initial; - overflow: visible; - max-height: none; + overflow: hidden; + max-height: 360px; + padding-top: 0.25rem; } .todo-checklist-preview { @@ -2445,7 +2625,7 @@ body.view-todo .note-card.todo-card .todo-checklist-preview-wrap { .todo-checklist-preview-item { display: flex; - align-items: center; + align-items: flex-start; gap: 0.6rem; font-size: 0.95rem; line-height: 1.35; @@ -2461,6 +2641,7 @@ body.view-todo .note-card.todo-card .todo-checklist-preview-wrap { justify-content: center; flex: 0 0 auto; opacity: 0.7; + margin-top: 2px; } .todo-checklist-preview-box i { @@ -2468,6 +2649,14 @@ body.view-todo .note-card.todo-card .todo-checklist-preview-wrap { opacity: 0; } +.todo-checklist-preview-text { + flex: 1; + min-width: 0; + overflow-wrap: break-word; + word-wrap: break-word; + word-break: break-word; +} + .todo-checklist-preview-item.is-checked { opacity: 0.7; } diff --git a/shaarli-pro/js/custom_views.js b/shaarli-pro/js/custom_views.js index 5e56696..9f0b0eb 100644 --- a/shaarli-pro/js/custom_views.js +++ b/shaarli-pro/js/custom_views.js @@ -12,13 +12,13 @@ document.addEventListener("DOMContentLoaded", function () { const activeTags = (searchTagsRaw + " " + pathTagRaw).toLowerCase().split(/[\s,]+/).filter(t => t); // Foolproof detection using sidebar active state and DOM rendered tags - const hasNoteActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Notes"].active, .header-nav-link[aria-label="Notes"].active, .sidebar-link[href*="searchtags=note"].active'); + const hasNoteActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Notes"].active, .header-nav-link[aria-label="Notes"].active, .sidebar-link[href*="searchtags=note"].active, .sidebar-link[href*="searchtags=shaarli-note"].active'); const hasTodoActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Mes tâches"].active, .header-nav-link[aria-label="Mes tâches"].active, .sidebar-link[href*="searchtags=shaarli-todo"].active'); const hasArchiveActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Archive"].active, .header-nav-link[aria-label="Archive"].active, .sidebar-link[href*="searchtags=shaarli-archive"].active'); const domChipTags = Array.from(document.querySelectorAll('.search-tag-chip')).map(el => (el.textContent || "").trim().toLowerCase()); - const isNoteView = activeTags.includes("note") || hasNoteActiveMenu || domChipTags.includes("note"); + const isNoteView = activeTags.includes("note") || activeTags.includes("shaarli-note") || hasNoteActiveMenu || domChipTags.includes("note") || domChipTags.includes("shaarli-note"); const isTodoView = activeTags.includes("shaarli-todo") || hasTodoActiveMenu || domChipTags.includes("shaarli-todo"); const isArchiveView = activeTags.includes("shaarli-archive") || hasArchiveActiveMenu || domChipTags.includes("shaarli-archive"); @@ -49,21 +49,205 @@ document.addEventListener("DOMContentLoaded", function () { document.querySelectorAll(".notes-wrapper.todo-wrapper").forEach((el) => el.remove()); }; - // Fonction de rendu Markdown basique pour l'affichage des notes + // Rendu Markdown étendu pour les cartes Notes/Tâches function renderMarkdown(markdown) { if (!markdown) return ""; - let html = markdown - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/^### (.*$)/gim, "
${code}`);
+ return token;
+ });
+
+ out = out.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, rawUrl) => {
+ const url = sanitizeUrl(rawUrl);
+ if (!url) return _;
+ return `${escape(codeLines.join("\n"))}`);
+ inCode = false;
+ codeLang = "";
+ codeLines = [];
+ }
+ return;
+ }
+
+ if (inCode) {
+ codeLines.push(line);
+ return;
+ }
+
+ if (!trimmed) {
+ closeLists();
+ html.push('');
+ return;
+ }
+
+ const headingMatch = line.match(/^\s*(#{1,6})\s+(.+)$/);
+ if (headingMatch) {
+ closeLists();
+ const level = headingMatch[1].length;
+ html.push(`${renderInline(quoteMatch[1])}`); + return; + } + + const todoMatch = line.match(/^\s*[-*]\s+\[( |x|X)\]\s+(.+)$/); + if (todoMatch) { + if (inUl) { + html.push(""); + inUl = false; + } + if (inOl) { + html.push(""); + inOl = false; + } + if (!inTodo) { + html.push('
${renderInline(line)}
`); + }); + + if (inCode) { + const langAttr = codeLang ? ` data-lang="${escapeAttr(codeLang)}"` : ""; + html.push(`${escape(codeLines.join("\n"))}`);
+ }
+
+ closeLists();
+ return html.join("");
}
function applyKeepNoteFormatting(textarea, format) {
@@ -73,12 +257,16 @@ document.addEventListener("DOMContentLoaded", function () {
const value = String(textarea.value || "");
const selected = value.slice(start, end);
- const replaceRange = (from, to, replacement) => {
+ const replaceRange = (from, to, replacement, caretMode = "end") => {
const next = value.slice(0, from) + replacement + value.slice(to);
textarea.value = next;
- const cursor = from + replacement.length;
textarea.focus({ preventScroll: true });
- textarea.setSelectionRange(cursor, cursor);
+ if (caretMode === "select") {
+ textarea.setSelectionRange(from, from + replacement.length);
+ } else {
+ const cursor = from + replacement.length;
+ textarea.setSelectionRange(cursor, cursor);
+ }
};
const wrapSelection = (prefix, suffix) => {
@@ -101,50 +289,121 @@ document.addEventListener("DOMContentLoaded", function () {
return { lineStart, lineEnd };
};
- if (format === "bold") return wrapSelection("**", "**");
- if (format === "italic") return wrapSelection("*", "*");
- if (format === "underline") return wrapSelection("", "");
-
- if (format === "h1" || format === "h2" || format === "p") {
+ const mapLines = (mapper) => {
const { lineStart, lineEnd } = lineRangeForSelection();
const block = value.slice(lineStart, lineEnd);
const lines = block.split(/\r?\n/);
- const prefix = format === "h1" ? "# " : format === "h2" ? "## " : "";
- const nextLines = lines.map((ln) => {
- const stripped = ln.replace(/^\s*(#{1,6}\s+)/, "");
- return prefix ? prefix + stripped : stripped;
- });
+ const nextLines = lines.map(mapper);
const replacement = nextLines.join("\n");
const next = value.slice(0, lineStart) + replacement + value.slice(lineEnd);
textarea.value = next;
textarea.focus({ preventScroll: true });
textarea.setSelectionRange(lineStart, lineStart + replacement.length);
+ };
+
+ if (format === "bold") return wrapSelection("**", "**");
+ if (format === "italic") return wrapSelection("*", "*");
+ if (format === "underline") return wrapSelection("", "");
+ if (format === "strike") return wrapSelection("~~", "~~");
+ if (format === "code") return wrapSelection("`", "`");
+
+ if (format === "link") {
+ const label = selected || "Texte du lien";
+ const replacement = `[${label}](https://)`;
+ replaceRange(start, end, replacement);
+ const cursor = start + replacement.length - 1;
+ textarea.setSelectionRange(cursor, cursor);
return;
}
+ if (format === "image") {
+ const alt = selected || "Description image";
+ const replacement = ``;
+ replaceRange(start, end, replacement);
+ const cursor = start + replacement.length - 1;
+ textarea.setSelectionRange(cursor, cursor);
+ return;
+ }
+
+ if (format === "quote") {
+ return mapLines((ln) => (ln.startsWith("> ") ? ln : `> ${ln}`));
+ }
+
+ if (format === "ul") {
+ return mapLines((ln) => {
+ const trimmed = ln.trim();
+ if (!trimmed) return "- ";
+ if (/^[-*+]\s+/.test(trimmed)) return ln;
+ return `- ${trimmed}`;
+ });
+ }
+
+ if (format === "ol") {
+ return mapLines((ln, idx) => {
+ const trimmed = ln.trim();
+ if (!trimmed) return `${idx + 1}. `;
+ if (/^\d+\.\s+/.test(trimmed)) return ln;
+ return `${idx + 1}. ${trimmed}`;
+ });
+ }
+
+ if (format === "todo") {
+ return mapLines((ln) => {
+ const trimmed = ln.trim();
+ if (!trimmed) return "- [ ] ";
+ if (/^[-*]\s+\[( |x|X)\]\s+/.test(trimmed)) return ln;
+ return `- [ ] ${trimmed}`;
+ });
+ }
+
+ if (format === "codeblock") {
+ const body = selected || "code";
+ const replacement = `\n\`\`\`\n${body}\n\`\`\`\n`;
+ replaceRange(start, end, replacement, "select");
+ return;
+ }
+
+ if (format === "h1" || format === "h2" || format === "h3" || format === "p") {
+ const prefix = format === "h1" ? "# " : format === "h2" ? "## " : format === "h3" ? "### " : "";
+ return mapLines((ln) => {
+ const stripped = ln.replace(/^\s*(#{1,6}\s+)/, "");
+ return prefix ? prefix + stripped : stripped;
+ });
+ }
+
if (format === "clear") {
if (!selected) {
- const { lineStart, lineEnd } = lineRangeForSelection();
- const block = value.slice(lineStart, lineEnd);
- const cleaned = block
- .replace(/^\s*(#{1,6}\s+)/gm, "")
- .replace(/\*\*(.*?)\*\*/g, "$1")
- .replace(/\*(.*?)\*/g, "$1")
- .replace(/(.*?)<\/u>/g, "$1");
- const next = value.slice(0, lineStart) + cleaned + value.slice(lineEnd);
- textarea.value = next;
- textarea.focus({ preventScroll: true });
- textarea.setSelectionRange(lineStart, lineStart + cleaned.length);
- return;
+ return mapLines((ln) =>
+ ln
+ .replace(/^\s*(#{1,6}\s+)/, "")
+ .replace(/^\s*>\s?/, "")
+ .replace(/^\s*[-*+]\s+\[( |x|X)\]\s+/, "")
+ .replace(/^\s*[-*+]\s+/, "")
+ .replace(/^\s*\d+\.\s+/, "")
+ .replace(/\*\*(.*?)\*\*/g, "$1")
+ .replace(/__(.*?)__/g, "$1")
+ .replace(/~~(.*?)~~/g, "$1")
+ .replace(/\*(.*?)\*/g, "$1")
+ .replace(/_(.*?)_/g, "$1")
+ .replace(/`(.*?)`/g, "$1")
+ .replace(/(.*?)<\/u>/g, "$1"),
+ );
}
const cleaned = selected
.replace(/\*\*(.*?)\*\*/g, "$1")
+ .replace(/__(.*?)__/g, "$1")
+ .replace(/~~(.*?)~~/g, "$1")
.replace(/\*(.*?)\*/g, "$1")
+ .replace(/_(.*?)_/g, "$1")
+ .replace(/`(.*?)`/g, "$1")
.replace(/(.*?)<\/u>/g, "$1");
replaceRange(start, end, cleaned);
}
}
+ window.renderMarkdown = renderMarkdown;
+ window.applyKeepNoteFormatting = applyKeepNoteFormatting;
+
const startViewInitialization = function () {
if (typeof initBookmarkPaletteButtons === "function") {
initBookmarkPaletteButtons();
@@ -223,6 +482,20 @@ document.addEventListener("DOMContentLoaded", function () {
}
});
+function renderMarkdown(markdown) {
+ if (typeof window !== "undefined" && typeof window.renderMarkdown === "function") {
+ return window.renderMarkdown(markdown);
+ }
+ return String(markdown || "")
+ .replace(/\n/g, "