diff --git a/public/pages/notes.js b/public/pages/notes.js index 68a97d3..c9f032c 100644 --- a/public/pages/notes.js +++ b/public/pages/notes.js @@ -149,6 +149,53 @@ function renderNoteCard(note) { `; } +// -------------------------------------------------------- +// Formatierungs-Helfer +// -------------------------------------------------------- + +function applyFormat(textarea, format) { + const start = textarea.selectionStart; + const end = textarea.selectionEnd; + const text = textarea.value; + const sel = text.slice(start, end); + + let before, after, insert; + switch (format) { + case 'bold': + before = '**'; after = '**'; + insert = sel || 'Text'; + break; + case 'italic': + before = '*'; after = '*'; + insert = sel || 'Text'; + break; + case 'list': { + // Jede Zeile mit "- " prefixen oder neuen Listenpunkt einfügen + if (sel) { + const lines = sel.split('\n').map((l) => l.startsWith('- ') ? l : `- ${l}`); + textarea.setRangeText(lines.join('\n'), start, end, 'end'); + return; + } + before = ''; after = ''; + const lineStart = text.lastIndexOf('\n', start - 1) + 1; + const currentLine = text.slice(lineStart, start); + if (currentLine.trim() === '') { + textarea.setRangeText('- ', start, start, 'end'); + } else { + textarea.setRangeText('\n- ', start, start, 'end'); + } + return; + } + default: return; + } + + const replacement = `${before}${insert}${after}`; + textarea.setRangeText(replacement, start, end, 'select'); + // Selektion auf den eingefügten Text setzen (ohne Marker) + textarea.selectionStart = start + before.length; + textarea.selectionEnd = start + before.length + insert.length; +} + // -------------------------------------------------------- // Modal // -------------------------------------------------------- @@ -165,8 +212,20 @@ function openNoteModal({ mode, note = null }) {
+
+ + + + +
@@ -205,6 +264,22 @@ function openNoteModal({ mode, note = null }) { }); }); + // Formatierungs-Toolbar + const textarea = panel.querySelector('#note-content'); + panel.querySelectorAll('.note-format-btn[data-format]').forEach((btn) => { + btn.addEventListener('click', () => { + applyFormat(textarea, btn.dataset.format); + textarea.focus(); + }); + }); + + textarea.addEventListener('keydown', (e) => { + if (e.ctrlKey || e.metaKey) { + if (e.key === 'b') { e.preventDefault(); applyFormat(textarea, 'bold'); } + if (e.key === 'i') { e.preventDefault(); applyFormat(textarea, 'italic'); } + } + }); + panel.querySelector('#note-modal-cancel').addEventListener('click', closeModal); panel.querySelector('#note-modal-save').addEventListener('click', async () => { diff --git a/public/styles/notes.css b/public/styles/notes.css index b944cec..baf8e44 100644 --- a/public/styles/notes.css +++ b/public/styles/notes.css @@ -101,7 +101,7 @@ position: relative; opacity: 0; transition: opacity var(--transition-fast), background-color var(--transition-fast); - color: var(--color-text-primary); + color: inherit; } .note-card__pin::before { @@ -127,14 +127,15 @@ font-size: var(--text-sm); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-1); - color: var(--color-text-primary); + color: inherit; padding-right: var(--space-6); } .note-card__content { font-size: var(--text-sm); line-height: var(--line-height-relaxed); - color: var(--color-text-secondary); + color: inherit; + opacity: 0.78; word-break: break-word; white-space: pre-wrap; } @@ -149,7 +150,8 @@ justify-content: space-between; margin-top: var(--space-2); padding-top: var(--space-2); - border-top: 1px solid var(--color-border-subtle); + border-top: 1px solid currentColor; + opacity: 0.55; } .note-card__creator { @@ -157,7 +159,7 @@ align-items: center; gap: var(--space-1); font-size: var(--text-xs); - color: var(--color-text-tertiary); + color: inherit; } .note-card__avatar { @@ -184,7 +186,7 @@ align-items: center; justify-content: center; position: relative; - color: var(--color-text-tertiary); + color: inherit; opacity: 0; transition: opacity var(--transition-fast), background-color var(--transition-fast); } @@ -256,3 +258,54 @@ .note-color-swatch:hover { transform: scale(1.15); } .note-color-swatch--active { border-color: var(--color-text-primary); transform: scale(1.1); } +/* -------------------------------------------------------- + * Formatierungs-Toolbar im Notiz-Editor + * -------------------------------------------------------- */ +.note-format-toolbar { + display: flex; + gap: var(--space-1); + padding: var(--space-1) var(--space-2); + border: 1.5px solid var(--color-border); + border-bottom: none; + border-radius: var(--radius-sm) var(--radius-sm) 0 0; + background-color: var(--color-surface-2); +} + +.note-format-toolbar + .form-input { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.note-format-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: none; + border-radius: var(--radius-xs); + background: transparent; + color: var(--color-text-secondary); + cursor: pointer; + transition: background-color var(--transition-fast), color var(--transition-fast); + font-size: var(--text-sm); + padding: 0; +} + +.note-format-btn:hover { + background-color: var(--color-accent-light); + color: var(--color-accent); +} + +.note-format-btn--sep { + width: 1px; + background-color: var(--color-border); + margin: var(--space-1) var(--space-1); + cursor: default; +} + +.note-format-btn--sep:hover { + background-color: var(--color-border); + color: var(--color-text-secondary); +} + diff --git a/public/sw.js b/public/sw.js index 61d33b1..68b36d6 100644 --- a/public/sw.js +++ b/public/sw.js @@ -12,9 +12,9 @@ * API: Immer Netzwerk (kein Caching von Nutzerdaten) */ -const SHELL_CACHE = 'oikos-shell-v17'; -const PAGES_CACHE = 'oikos-pages-v17'; -const ASSETS_CACHE = 'oikos-assets-v17'; +const SHELL_CACHE = 'oikos-shell-v18'; +const PAGES_CACHE = 'oikos-pages-v18'; +const ASSETS_CACHE = 'oikos-assets-v18'; const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, ASSETS_CACHE]; // App-Shell: sofort benötigt für ersten Render