feat(autosave): grava por ociosidade (10s padrão, configurável) e em silêncio
- debounce de 1,5s → 10s SEM nenhuma alteração (cada tecla reinicia); seletor 5/10/30/60s no diálogo de Preferências (autosaveDelay, clamp 5–120) - autosave silencioso: sem toast, só o status 'salvando…/salvo'; Ctrl+S manual mantém o toast com o hash da revisão - flush de pendências ao trocar de aba do app e em visibilitychange=hidden /pagehide (melhor esforço) — nada se perde ao sair antes do idle - README atualizado
This commit is contained in:
1 parent
993b341fc1
commit
11fc26390d
3 files changed
+35
-9
No files matched your search
+8
-1
@@ -34,7 +34,14 @@
|
||||
<label class="row"><input type="checkbox" id="pf-colmark"> Marcar coluna
|
||||
<input type="number" id="pf-col" min="20" max="320" step="1" class="num"> (padrão 80)</label>
|
||||
<label class="row"><input type="checkbox" id="pf-hl"> Colorir sintaxe (Markdown e HTML)</label>
|
||||
<label class="row"><input type="checkbox" id="pf-autosave"> Autosave — salva automaticamente ~1,5 s após parar de digitar</label>
|
||||
<label class="row"><input type="checkbox" id="pf-autosave"> Autosave — salva após período de inatividade
|
||||
<select id="pf-autosave-delay" class="compact">
|
||||
<option value="5">5 s</option>
|
||||
<option value="10" selected>10 s</option>
|
||||
<option value="30">30 s</option>
|
||||
<option value="60">60 s</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="row">Tecla Tab
|
||||
<select id="pf-tabmode" class="compact">
|
||||
<option value="spaces">insere espaços</option>
|
||||
|
||||
+21
-5
@@ -287,7 +287,10 @@
|
||||
|
||||
function activateTab(key) {
|
||||
const tab = state.tabs.find((t) => t.key === key);
|
||||
if (!tab) return;
|
||||
if (!tab || tab.key === state.activeKey) return;
|
||||
// trocar de aba = fim da edição da anterior: grava pendência (silencioso)
|
||||
const prev = activeTab();
|
||||
if (prev && prev.dirty && !prev.conflictPending && prefs.autosave) save(true);
|
||||
state.activeKey = key;
|
||||
state.selectedRev = null;
|
||||
fillEditor();
|
||||
@@ -773,13 +776,21 @@
|
||||
}
|
||||
|
||||
let autosaveTimer = 0;
|
||||
// Ociosidade: só grava após N segundos SEM nenhuma alteração; cada tecla
|
||||
// reinicia a contagem. Ao perder o foco da janela, a pendência é gravada.
|
||||
function scheduleAutosave() {
|
||||
if (!prefs.autosave) return;
|
||||
clearTimeout(autosaveTimer);
|
||||
autosaveTimer = setTimeout(() => {
|
||||
const t = activeTab();
|
||||
if (t && t.dirty && !t.conflictPending) save(true);
|
||||
}, 1500);
|
||||
}, Math.max(5, prefs.autosaveDelay) * 1000);
|
||||
}
|
||||
|
||||
function flushAutosave() {
|
||||
clearTimeout(autosaveTimer);
|
||||
const t = activeTab();
|
||||
if (t && t.dirty && !t.conflictPending) save(true);
|
||||
}
|
||||
|
||||
E.editor.addEventListener('input', () => {
|
||||
@@ -947,7 +958,7 @@
|
||||
/* ------------------------------------------------------- preferências */
|
||||
|
||||
const PREFS_KEY = 'notas_prefs';
|
||||
const PREFS_DEFAULT = { gutter: true, wrap: true, colmark: true, col: 80, hl: true, autosave: true, tabSpaces: true, tabSize: 4, font: 'mono', size: 14, theme: 'claro' };
|
||||
const PREFS_DEFAULT = { gutter: true, wrap: true, colmark: true, col: 80, hl: true, autosave: true, autosaveDelay: 10, tabSpaces: true, tabSize: 4, font: 'mono', size: 14, theme: 'claro' };
|
||||
const THEMES = ['claro', 'escuro', 'solar-claro', 'solar-escuro',
|
||||
// iTerm2-Color-Schemes (mbadolato, MIT)
|
||||
'dracula', 'nord', 'one-dark', 'monokai', 'gruvbox-dark', 'tokyo-night',
|
||||
@@ -973,6 +984,7 @@
|
||||
p.col = Math.min(320, Math.max(20, Math.round(p.col) || 80));
|
||||
p.size = Math.min(32, Math.max(10, Math.round(p.size) || 14));
|
||||
p.tabSize = Math.min(16, Math.max(1, Math.round(p.tabSize) || 4));
|
||||
p.autosaveDelay = Math.min(120, Math.max(5, Math.round(p.autosaveDelay) || 10));
|
||||
if (!FONT_STACKS[p.font]) p.font = 'mono';
|
||||
if (!THEMES.includes(p.theme)) p.theme = 'claro';
|
||||
return p;
|
||||
@@ -1201,7 +1213,7 @@
|
||||
const PF = {
|
||||
overlay: $('#prefs-overlay'), form: $('#prefs-form'), gutter: $('#pf-gutter'),
|
||||
wrap: $('#pf-wrap'), colmark: $('#pf-colmark'), col: $('#pf-col'), hl: $('#pf-hl'),
|
||||
autosave: $('#pf-autosave'),
|
||||
autosave: $('#pf-autosave'), autosaveDelay: $('#pf-autosave-delay'),
|
||||
tabmode: $('#pf-tabmode'), tabsize: $('#pf-tabsize'),
|
||||
font: $('#pf-font'), size: $('#pf-size'), theme: $('#pf-theme'),
|
||||
reset: $('#pf-reset'), cancel: $('#pf-cancel'),
|
||||
@@ -1215,6 +1227,7 @@
|
||||
PF.col.value = prefs.col;
|
||||
PF.hl.checked = prefs.hl;
|
||||
PF.autosave.checked = prefs.autosave;
|
||||
PF.autosaveDelay.value = String(prefs.autosaveDelay);
|
||||
PF.tabmode.value = prefs.tabSpaces ? 'spaces' : 'tab';
|
||||
PF.tabsize.value = prefs.tabSize;
|
||||
PF.font.value = prefs.font;
|
||||
@@ -1229,6 +1242,7 @@
|
||||
prefs.col = Math.min(320, Math.max(20, parseInt(PF.col.value, 10) || 80));
|
||||
prefs.hl = PF.hl.checked;
|
||||
prefs.autosave = PF.autosave.checked;
|
||||
prefs.autosaveDelay = Math.min(120, Math.max(5, parseInt(PF.autosaveDelay.value, 10) || 10));
|
||||
prefs.tabSpaces = PF.tabmode.value !== 'tab';
|
||||
prefs.tabSize = Math.min(16, Math.max(1, parseInt(PF.tabsize.value, 10) || 4));
|
||||
prefs.font = FONT_STACKS[PF.font.value] ? PF.font.value : 'mono';
|
||||
@@ -1635,7 +1649,7 @@
|
||||
loadNotes().catch(() => {});
|
||||
loadAggregates().catch(() => {});
|
||||
if (!E.versionsPanel.classList.contains('hidden')) loadRevisions().catch(() => {});
|
||||
toast('Salvo — revisão ' + data.hash.slice(0, 8));
|
||||
if (!auto) toast('Salvo — revisão ' + data.hash.slice(0, 8));
|
||||
}
|
||||
|
||||
// Conflito: o servidor mudou desde a base da aba. Recarregar ou sobrescrever.
|
||||
@@ -1873,6 +1887,7 @@
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') checkFreshness();
|
||||
else flushAutosave(); // janela minimizada/trocou de app: grava pendências já
|
||||
});
|
||||
window.addEventListener('focus', checkFreshness);
|
||||
|
||||
@@ -1937,6 +1952,7 @@
|
||||
}
|
||||
|
||||
window.addEventListener('pagehide', () => {
|
||||
flushAutosave(); // melhor esforço: grava antes de a página morrer
|
||||
// remove a sessão do registro assim que a aba fecha (keepalive sobrevive ao unload)
|
||||
fetch('/api/presence', {
|
||||
method: 'POST',
|
||||
|
||||
Reference in new issue
Block a user