diff --git a/js/app.js b/js/app.js index a18b30f..53171d6 100644 --- a/js/app.js +++ b/js/app.js @@ -4713,7 +4713,17 @@ function soDirtyCheck() { if (btn) btn.style.display = isSoDirty() ? '' : 'none'; } +// Re-entrancy latch: a double-click on Save (or Enter+click) while the first +// run awaits encryption/POST would fire a SECOND full save → duplicate entry. +// Same async-race class as the sync-button guard. +let soSaving = false; async function soSave() { + if (soSaving) return; + soSaving = true; + try { await soSaveInner(); } finally { soSaving = false; } +} + +async function soSaveInner() { if (!soState) return; // Flush any uncommitted tag text — user may have typed in the chip // input without pressing Enter / comma before clicking Save.