mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-03 16:41:27 +01:00
revisions: a save keeps what it replaces
Every write of a document's text keeps the text it replaces, in the same transaction. Autosave writes every few seconds, so an ordinary save keeps one at most every ten minutes; `:w!`, a save over the SFTP mount and a restore always keep one, because each may undo something. A rename alone, or a document that was empty, keeps nothing. `gh` or `:history` lists the open document's earlier versions, the newest first, and shows the highlighted one as a line diff against the text as it is now. Enter brings it back, and what it replaces becomes a version of its own, so a restore is undone the same way. The daily sweep forgets versions older than WORKSPACE_REVISION_DAYS (90) but never the newest 20 of a document, and keeps an upload as long as a version still embeds it. Versions stay out of the export, the mount and the readable mirror; a document in the trash shows none.
This commit is contained in:
1 parent
8ffad6350f
commit
fc2be66b0c
17 files changed
+887
-31
No files matched your search
+35
-1
@@ -15,6 +15,7 @@
|
||||
import Overview from './lib/Overview.svelte'
|
||||
import MediaBrowser, { copyMedia, insertMedia, removeMedia } from './lib/MediaBrowser.svelte'
|
||||
import Trash, { purgeTrashed, restoreTrashed } from './lib/Trash.svelte'
|
||||
import History, { moveHistory, restoreShown } from './lib/History.svelte'
|
||||
import Actions from './lib/Actions.svelte'
|
||||
import Prompt from './lib/Prompt.svelte'
|
||||
import SecretPrompt from './lib/SecretPrompt.svelte'
|
||||
@@ -421,6 +422,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** The history's keys: j/k through the versions, Enter brings the one shown back. */
|
||||
function historyKey(event: KeyboardEvent): boolean {
|
||||
switch (event.key) {
|
||||
case 'j':
|
||||
case 'ArrowDown':
|
||||
moveHistory(1)
|
||||
return true
|
||||
case 'k':
|
||||
case 'ArrowUp':
|
||||
moveHistory(-1)
|
||||
return true
|
||||
case 'G':
|
||||
moveHistory(workspace.history.length)
|
||||
return true
|
||||
case 'Enter':
|
||||
void restoreShown()
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* `F`: the hints are pinned where the targets are, so a pane still sliding
|
||||
* in — a space switch, the board just opened — is let settle first.
|
||||
@@ -608,6 +631,12 @@
|
||||
return
|
||||
}
|
||||
}
|
||||
if (workspace.overlay === 'history' && listening && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
||||
if (historyKey(event)) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (workspace.overlay === 'trash' && listening && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
||||
if (trashKey(event)) {
|
||||
event.preventDefault()
|
||||
@@ -759,6 +788,11 @@
|
||||
void runCommand('outline')
|
||||
return
|
||||
}
|
||||
if (previous === 'g' && key === 'h') {
|
||||
event.preventDefault()
|
||||
void runCommand('history')
|
||||
return
|
||||
}
|
||||
if (previous === 'g' && key === 'x') {
|
||||
event.preventDefault()
|
||||
openInTab()
|
||||
@@ -1041,7 +1075,7 @@
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions, a11y_no_noninteractive_tabindex -->
|
||||
<div class="overlay" tabindex="-1" bind:this={overlayEl} onclick={(e) => e.target === e.currentTarget && (workspace.overlay = 'none')}
|
||||
>
|
||||
{#if workspace.overlay === 'help'}<Help />{:else if workspace.overlay === 'overview'}<Overview />{:else if workspace.overlay === 'agenda'}<Agenda />{:else if workspace.overlay === 'actions'}<Actions />{:else if workspace.overlay === 'verify'}<Verify />{:else if workspace.overlay === 'outline'}<Headings />{:else if workspace.overlay === 'trash'}<Trash />{:else}<MediaBrowser />{/if}
|
||||
{#if workspace.overlay === 'help'}<Help />{:else if workspace.overlay === 'overview'}<Overview />{:else if workspace.overlay === 'agenda'}<Agenda />{:else if workspace.overlay === 'actions'}<Actions />{:else if workspace.overlay === 'verify'}<Verify />{:else if workspace.overlay === 'outline'}<Headings />{:else if workspace.overlay === 'trash'}<Trash />{:else if workspace.overlay === 'history'}<History />{:else}<MediaBrowser />{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user