From 48b5907f4cdee958db3997e486710a68f43b9485 Mon Sep 17 00:00:00 2001 From: th Date: Sat, 18 Apr 2026 10:04:08 +0200 Subject: [PATCH] =?UTF-8?q?v2.0.2=20=E2=80=94=20English=20tag=20aliases,?= =?UTF-8?q?=20English-only=20website,=20undo-body=20flaky=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tags: accept #birthday and #appointment as English synonyms for #geburtstag and #termin. Normalised to the German canonical form at extractTags() write-time so every downstream check (parseTags / calendar logic / board filters) keeps working without per-call-site aliasing. Editor body-regex extended so the "new appointment" heuristic and auto-sync trigger on either variant. - website: replace #termin/#geburtstag with #appointment/#birthday in tag chips, calendar feature list, and calendar legend. - tests: fix flaky "Undo: Editor Save → Ctrl+Z reverts body change" that raced doSave(). The test polled the server for the patched body and fired Ctrl+Z as soon as it landed, but the undo shortcut is guarded by !guards.isOpen('modal'), and closeModal() only runs after the post-patch _reloadAll — on fast servers the key was swallowed. Poll now also waits for the modal `.open` class to drop. Co-Authored-By: Claude Opus 4.7 (1M context) --- config.php | 2 +- electron/package.json | 2 +- public/index.php | 9 ++++++++- public/js/modules/editor.js | 4 ++-- public/plugins/tests/undo.test.js | 8 +++++++- website/index.html | 12 ++++++------ 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/config.php b/config.php index 20f7176..8b2564b 100644 --- a/config.php +++ b/config.php @@ -1,7 +1,7 @@ 'geburtstag', 'appointment' => 'termin']; + $tags = array_map(fn($t) => $aliases[$t] ?? $t, array_map('strtolower', $matches[1] ?? [])); + return implode(',', array_unique($tags)); } // Plugin route handlers (registered by plugin backend.php files). diff --git a/public/js/modules/editor.js b/public/js/modules/editor.js index bd7ebb4..9750cad 100644 --- a/public/js/modules/editor.js +++ b/public/js/modules/editor.js @@ -234,7 +234,7 @@ export function openModal(card = null) { // `card` can be `null` (add-button new) or a shell `{id: null, ...}` // from the calendar day-popup — both count as "new", so key on // card?.id not the card ref itself. - const isNewAppointment = !card?.id && /#(termin|geburtstag)\b/.test(body.value); + const isNewAppointment = !card?.id && /#(termin|appointment|geburtstag|birthday)\b/.test(body.value); inpBucket.value = card?.bucket_id ? card.bucket_id : (isNewAppointment ? '' : (!virtualBuckets.includes(state.activeBucket) ? state.activeBucket : '')); @@ -512,7 +512,7 @@ async function doSave(close, { animate = true } = {}) { } await window._reloadAll(); // Auto-sync if card has #termin or #geburtstag - if (data.body && /#(termin|geburtstag)\b/.test(data.body)) { + if (data.body && /#(termin|appointment|geburtstag|birthday)\b/.test(data.body)) { window._triggerSync?.(); } if (close) { diff --git a/public/plugins/tests/undo.test.js b/public/plugins/tests/undo.test.js index a3aeab6..aecca9c 100644 --- a/public/plugins/tests/undo.test.js +++ b/public/plugins/tests/undo.test.js @@ -109,11 +109,17 @@ describe('Undo: Editor Save', () => { bodyEl.dispatchEvent(new Event('input', { bubbles: true })); click('#modal-save-close'); + // Wait for both the server patch AND the modal close before firing + // Ctrl+Z — the global undo shortcut is guarded by + // `!guards.isOpen('modal')`, so if doSave's closeModal() hasn't run + // yet (it only fires after the post-save _reloadAll), the undo key + // is swallowed silently and the test races the client's save flow. let card; for (let i = 0; i < 40; i++) { await sleep(100); card = (await fetch(`api/cards?ids=${res.id}`).then(r => r.json())).find(c => c.id == res.id); - if ((card?.body || '').includes('after edit')) break; + const modalClosed = !document.getElementById('modal')?.classList.contains('open'); + if ((card?.body || '').includes('after edit') && modalClosed) break; } expect(card.body).toContain('after edit'); diff --git a/website/index.html b/website/index.html index c764499..f871e50 100644 --- a/website/index.html +++ b/website/index.html @@ -352,10 +352,10 @@ footer { #ops #done #meeting - #termin + #appointment #research #idea - #geburtstag + #birthday #personal @@ -407,8 +407,8 @@ See also: [[Infra checklist]]

A month-grid calendar built into hyprcrd. Journal entries, appointments, and birthdays — all just cards with the right title or tag. Bidirectional CalDAV sync with ownCloud and Nextcloud.