Auto-save drafts to localStorage

- Drafts saved 1s after last keystroke (debounced)
- 'Draft saved' indicator in toolbar
- On file open: prompts to restore unsaved draft if found
- Draft cleared on successful save (Ctrl+S)
- Works across disconnects/logouts — draft persists in browser
This commit is contained in:
2026-05-27 10:31:47 +02:00
parent b2f3aa39af
commit f46f57eded
+44 -2
View File
@@ -74,6 +74,7 @@
</div>
<div class="toolbar-right">
<span class="file-name">{{ currentFile || 'No file open' }}</span>
<span v-if="draftSaved" class="draft-indicator">Draft saved</span>
<span class="file-meta" v-if="fileMeta">{{ fileMeta }}</span>
<button v-if="currentFile" class="toolbar-btn" @click="toggleCollab" :title="collabActive ? 'Disconnect collab' : 'Enable collab'">
{{ collabActive ? '👥 Live' : '👤 Solo' }}
@@ -99,7 +100,7 @@
<textarea
ref="editor"
v-model="content"
@input="isDirty = true"
@input="onEdit"
@keydown="handleKeydown"
@drop.prevent="handleImageDrop"
@dragover.prevent
@@ -110,7 +111,7 @@
<MilkdownEditor
:key="currentFile"
v-model="content"
@update:modelValue="isDirty = true"
@update:modelValue="onEdit"
/>
</div>
<div v-if="mode === 'split'" class="preview-pane">
@@ -514,14 +515,53 @@ async function openFile(path) {
}
}
isDirty.value = false
draftSaved.value = false
view.value = 'files'
showHistory.value = false
showShareDialog.value = false
aiResult.value = ''
// Check for unsaved draft
const draft = loadDraft()
if (draft && draft !== content.value) {
if (confirm('You have an unsaved draft for this file. Restore it?')) {
content.value = draft
isDirty.value = true
} else {
clearDraft()
}
}
loadHistory()
checkGitStatus()
}
// ─── Auto-draft ──────────────────────────────────────────────────────────────
const draftSaved = ref(false)
let draftTimer = null
function onEdit() {
isDirty.value = true
draftSaved.value = false
clearTimeout(draftTimer)
draftTimer = setTimeout(() => {
const key = currentFile.value || '_unsaved'
localStorage.setItem('mh_draft_' + key, content.value)
draftSaved.value = true
}, 1000)
}
function clearDraft() {
const key = currentFile.value || '_unsaved'
localStorage.removeItem('mh_draft_' + key)
draftSaved.value = false
}
function loadDraft() {
const key = currentFile.value || '_unsaved'
return localStorage.getItem('mh_draft_' + key)
}
async function saveFile() {
if (!currentFile.value) {
const name = prompt('Save as (e.g. notes.md):')
@@ -551,6 +591,7 @@ async function saveFile() {
}
cacheFile(currentFile.value, content.value)
isDirty.value = false
clearDraft()
setTimeout(checkGitStatus, 1000)
}
@@ -1200,6 +1241,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
}
.file-name { color: var(--text-muted); font-size: 13px; }
.draft-indicator { color: var(--success); font-size: 11px; opacity: 0.8; }
.export-actions { display: flex; gap: 4px; }
.export-actions button {