Skip to content

Commit 7644ffa

Browse files
committed
feat: fix template toggle with session hash + add confirmation dialog
- Extract clearCloudSession() helper in cloud-share.js - Call clearCloudSession() from selectTemplate() to clear stale session state - Add confirmation dialog before replacing editor content with a template - Confirmation only appears when editor has meaningful content - Undo (Ctrl+Z) recovers previous content after template load
1 parent aeca068 commit 7644ffa

File tree

3 files changed

+138
-9
lines changed

3 files changed

+138
-9
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Template Toggle Fix + Confirmation Dialog
2+
3+
## Summary
4+
5+
Fixed template switching bug when cloud session IDs are in the URL, and added a
6+
confirmation dialog before replacing editor content with a new template.
7+
8+
## Changes
9+
10+
### Bug Fix — Template toggle broken with cloud session hash
11+
12+
When a cloud auto-save session hash (`#id=...&k=...`) was in the URL, toggling
13+
between template categories stopped working correctly.
14+
15+
**Root cause:** `selectTemplate()` never cleared the shared-doc state
16+
(`readOnly`, `isViewingSharedDoc`, URL hash, cloud doc keys in localStorage).
17+
18+
**Fix:** Extracted a reusable `clearCloudSession()` helper in `cloud-share.js`
19+
that clears all shared-doc state and stops the cloud-save timer. Called from
20+
`selectTemplate()` in `templates.js`.
21+
22+
### Feature — Confirmation dialog before template replacement
23+
24+
When the user selects a template while the editor has meaningful content (non-empty
25+
and not the default Feature Showcase), a confirmation dialog now appears:
26+
27+
- **"Replace editor content?"** with the template name
28+
- **Cancel** button preserves current content
29+
- **Use Template** button loads the template
30+
- Undo hint reminds user they can `Ctrl+Z` to recover
31+
- Styled consistently with the existing clear-confirm overlay
32+
33+
If the editor is empty or has the default content, the template loads immediately
34+
without prompting.
35+
36+
## Files Modified
37+
38+
- `js/cloud-share.js` — Extracted `clearCloudSession()`, refactored banner handlers
39+
- `js/templates.js` — Added confirmation dialog, refactored into `loadTemplate()` + `showTemplateConfirm()` + `selectTemplate()`
40+
41+
## Testing
42+
43+
- 13/13 Playwright tests pass (8 UI panel + 5 integration)
44+
- Browser-verified: category toggling, session hash clearing, confirmation flow

js/cloud-share.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -363,23 +363,32 @@
363363
document.body.classList.remove('shared-view-active');
364364
M.markdownEditor.readOnly = false;
365365
}
366-
document.getElementById('shared-banner-edit').addEventListener('click', function () {
366+
367+
/**
368+
* Clear all cloud/shared-doc session state so subsequent edits create a
369+
* NEW document instead of overwriting the original shared one.
370+
* Called when: user clicks Edit/Close on shared banner, or loads a template.
371+
*/
372+
function clearCloudSession() {
367373
hideSharedBanner();
368-
// Clear shared state so edits create a NEW document, not overwrite the original
369374
M.isViewingSharedDoc = false;
370375
localStorage.removeItem(CLOUD_DOC_KEY);
371376
localStorage.removeItem(CLOUD_KEY_KEY);
372377
localStorage.removeItem(CLOUD_WT_KEY);
373378
window.history.replaceState({}, document.title, window.location.pathname);
379+
// Stop the cloud-save timer so it doesn't re-inject the session hash
380+
if (cloudSaveTimer) { clearInterval(cloudSaveTimer); cloudSaveTimer = null; }
381+
cloudSaveDirty = false;
382+
lastCloudContent = '';
383+
}
384+
M.clearCloudSession = clearCloudSession;
385+
386+
document.getElementById('shared-banner-edit').addEventListener('click', function () {
387+
clearCloudSession();
374388
M.setViewMode('split');
375389
});
376390
document.getElementById('shared-banner-close').addEventListener('click', function () {
377-
hideSharedBanner();
378-
M.isViewingSharedDoc = false;
379-
localStorage.removeItem(CLOUD_DOC_KEY);
380-
localStorage.removeItem(CLOUD_KEY_KEY);
381-
localStorage.removeItem(CLOUD_WT_KEY);
382-
window.history.replaceState({}, document.title, window.location.pathname);
391+
clearCloudSession();
383392
});
384393

385394
// --- Share Options Modal ---

js/templates.js

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,14 @@
304304
return true;
305305
}
306306

307-
function selectTemplate(tpl) {
307+
/**
308+
* Actually apply the template content into the editor (no confirmation).
309+
*/
310+
function loadTemplate(tpl) {
311+
// Clear any stale cloud/shared-doc session so the template loads cleanly
312+
// and cloud auto-save doesn't re-inject the old session hash into the URL.
313+
if (M.clearCloudSession) M.clearCloudSession();
314+
308315
let content = tpl.content;
309316

310317
// If the template defines variables, generate the block at the top
@@ -315,6 +322,9 @@
315322
// Resolve global built-in variables (date, time, etc.)
316323
content = resolveGlobalVariables(content);
317324

325+
// Push undo state so user can Ctrl+Z to recover previous content
326+
M.markdownEditor.dispatchEvent(new Event('input')); // ensure undo captures current state
327+
318328
M.markdownEditor.value = content;
319329
M.renderMarkdown();
320330
closeTemplateModal();
@@ -323,6 +333,72 @@
323333
M.markdownEditor.scrollTop = 0;
324334
}
325335

336+
/**
337+
* Show a confirmation overlay before replacing editor content with a template.
338+
* Styled consistently with the clear-confirm overlay in editor-features.js.
339+
*/
340+
function showTemplateConfirm(tpl) {
341+
// Remove any existing modal
342+
var old = document.getElementById('template-confirm-modal');
343+
if (old) old.remove();
344+
345+
var overlay = document.createElement('div');
346+
overlay.id = 'template-confirm-modal';
347+
overlay.className = 'clear-confirm-overlay';
348+
overlay.innerHTML =
349+
'<div class="clear-confirm-card">' +
350+
'<div class="clear-confirm-header">' +
351+
'<i class="bi bi-arrow-repeat"></i> Replace editor content?' +
352+
'</div>' +
353+
'<div class="clear-confirm-body">' +
354+
'<p>Loading <strong>' + tpl.name + '</strong> will replace your current work.</p>' +
355+
'<p class="clear-confirm-hint"><i class="bi bi-arrow-counterclockwise"></i> You can Undo with <kbd>Ctrl+Z</kbd></p>' +
356+
'</div>' +
357+
'<div class="clear-confirm-actions">' +
358+
'<button class="clear-confirm-cancel">Cancel</button>' +
359+
'<button class="clear-confirm-ok"><i class="bi bi-file-earmark-text"></i> Use Template</button>' +
360+
'</div>' +
361+
'</div>';
362+
363+
document.body.appendChild(overlay);
364+
requestAnimationFrame(function () { overlay.classList.add('active'); });
365+
366+
function close() {
367+
overlay.classList.remove('active');
368+
setTimeout(function () { overlay.remove(); }, 200);
369+
document.removeEventListener('keydown', escHandler);
370+
}
371+
372+
function escHandler(e) {
373+
if (e.key === 'Escape') { e.stopPropagation(); close(); }
374+
}
375+
document.addEventListener('keydown', escHandler, true);
376+
377+
overlay.addEventListener('click', function (e) { if (e.target === overlay) close(); });
378+
overlay.querySelector('.clear-confirm-cancel').addEventListener('click', close);
379+
overlay.querySelector('.clear-confirm-ok').addEventListener('click', function () {
380+
close();
381+
loadTemplate(tpl);
382+
});
383+
overlay.querySelector('.clear-confirm-cancel').focus();
384+
}
385+
386+
/**
387+
* Select a template: if editor has meaningful content, confirm first;
388+
* otherwise load immediately.
389+
*/
390+
function selectTemplate(tpl) {
391+
var editorContent = M.markdownEditor.value.trim();
392+
var defaultContent = (M.getDefaultContent ? M.getDefaultContent().trim() : '');
393+
var hasContent = editorContent.length > 0 && editorContent !== defaultContent;
394+
395+
if (hasContent) {
396+
showTemplateConfirm(tpl);
397+
} else {
398+
loadTemplate(tpl);
399+
}
400+
}
401+
326402
function openTemplateModal() {
327403
templateSearchInput.value = '';
328404
activeTemplateCategory = 'all';

0 commit comments

Comments
 (0)