mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-09-22 02:27:07 +00:00
Improve postponed conflict resolution handling
Keep unresolved conflicts visible after Not now, reopen them only on explicit requests, and clear stale warning and dialogue state when a replicated resolution arrives. Add typed provisional English messages, revision-tree regressions, specifications, and a focused Real Obsidian E2E.
This commit is contained in:
@@ -31,7 +31,18 @@ Commit the edited YAML and all regenerated JSON and TypeScript resources togethe
|
||||
|
||||
## Make a message translatable
|
||||
|
||||
1. Add its canonical English entry and translations to `src/common/messagesYAML/`.
|
||||
LiveSync-owned messages may first be added to
|
||||
`src/common/messages/LiveSyncProvisionalMessages.ts` while their wording is being
|
||||
exercised. This keeps an application-only message out of Commonlib and provides a
|
||||
typed English fallback without requiring contributors to update every language.
|
||||
|
||||
When the wording is ready for translation:
|
||||
|
||||
1. Move its canonical English entry from
|
||||
`src/common/messages/LiveSyncProvisionalMessages.ts` to
|
||||
`src/common/messagesYAML/en.yaml`. Remove the provisional entry in the same
|
||||
change. Translations in the other LiveSync YAML files may follow as contributor
|
||||
updates.
|
||||
2. Replace the source literal with `$msg()` or another existing translation helper, using the English catalogue key as the typed contract.
|
||||
3. Run `npm run i18n:bake`, build the plug-in, and verify the affected workflow.
|
||||
|
||||
|
||||
@@ -113,6 +113,8 @@ Self-hosted LiveSync owns the complete multilingual catalogue, generation tools,
|
||||
|
||||
The canonical Commonlib key type and English fallback change with Commonlib. LiveSync may add translations for those keys without duplicating every English definition; its translator delegates absent keys to Commonlib's canonical English fallback. A separate language package remains possible only if independent consumers and release cadence later justify it; core must never depend on an application catalogue.
|
||||
|
||||
LiveSync-owned wording may remain in a typed, application-local provisional English map while it is being exercised. The LiveSync translator composes those keys with the generated application catalogue and the Commonlib key type. Moving a stable message into LiveSync's YAML catalogue makes it available for translation without changing Commonlib.
|
||||
|
||||
### Svelte dialogue hosting
|
||||
|
||||
The present Svelte dialogue implementation is split into three responsibilities:
|
||||
|
||||
@@ -45,6 +45,27 @@ The all-branch history check prevents a resolved conflict from being recreated m
|
||||
|
||||
The compatibility implementation currently selects the newer modification time for differing binary conflicts even when the general **Always overwrite with a newer file** option is disabled. This is existing behaviour, not a new 1.0 guarantee. Changing it to explicit selection only is a separate compatibility decision.
|
||||
|
||||
### Two devices independently create the same path
|
||||
|
||||
If two devices create the same full synchronised path before either device has
|
||||
received the other creation, the two generation-one leaves have no shared
|
||||
revision. Files with the same name in different directories remain separate
|
||||
paths and do not form this conflict.
|
||||
|
||||
When the independently created files contain identical bytes, LiveSync deletes
|
||||
one duplicate leaf without synthesising merged content. A device which still
|
||||
records the deleted duplicate as its displayed revision already has the same
|
||||
bytes as the surviving revision, so it does not recreate the conflict. It
|
||||
rebinds its device-local provenance to the surviving revision.
|
||||
|
||||
When the independently created files contain different bytes, conservative
|
||||
three-way merge has no valid base. LiveSync therefore leaves the two versions
|
||||
for manual selection; it does not guess an empty base or concatenate unrelated
|
||||
files. If both versions instead descend from a revision which the devices had
|
||||
previously synchronised, they are ordinary divergent branches: LiveSync may
|
||||
merge non-overlapping text or structured-data changes from that shared base,
|
||||
and otherwise asks the user.
|
||||
|
||||
## Stale and concurrent resolutions
|
||||
|
||||
A device can resolve only the leaves which it has observed. If another device has already extended a branch, later replication can reveal another live leaf and require another resolution. Two devices can also produce different resolutions concurrently, leaving multiple live leaves after their trees meet.
|
||||
@@ -76,6 +97,30 @@ When no record exists, LiveSync may reconstruct the displayed revision only if t
|
||||
|
||||
If an edit's base cannot be proved, LiveSync keeps the bytes as another manual-resolution branch instead of attaching them silently to the database winner. If a deletion's displayed branch cannot be proved after the file body has gone, LiveSync preserves every branch and requests conflict review. For an unproven cross-path rename, the new target remains stored and every source branch is preserved for review. These fallbacks can leave a temporary duplicate or unresolved source, but they do not discard an unproven branch.
|
||||
|
||||
## Interactive dialogue policy
|
||||
|
||||
Choosing **Not now** postpones repeated merge dialogues for the same
|
||||
uninterrupted conflict episode in the current plug-in session. Ordinary file
|
||||
checks and replication do not reopen the dialogue while at least one conflict
|
||||
leaf remains. If the in-editor status display is enabled, the active file keeps
|
||||
the warning **This file has unresolved conflicts.** so that postponement does
|
||||
not make the conflict invisible.
|
||||
|
||||
The command **Resolve if conflicted.**, and selecting a file through **Pick a
|
||||
file to resolve conflict**, explicitly clear the postponement and request the
|
||||
dialogue again. Cancellation caused by another conflict dialogue does not count
|
||||
as **Not now**. Once the document has no remaining conflicts, the episode ends;
|
||||
a later conflict at the same path prompts normally. The postponement is not
|
||||
persisted across a plug-in reload.
|
||||
|
||||
When synchronisation supplies a resolved document, the existing incoming-file
|
||||
processing event closes an open conflict dialogue for that path. The same event
|
||||
rechecks the local revision tree: if no conflict leaf remains, it ends any
|
||||
postponed episode and removes the active-file warning. If another conflict leaf
|
||||
still exists, the stale dialogue closes, but the warning remains. A postponed
|
||||
episode stays postponed; otherwise, subsequent conflict processing may open a
|
||||
fresh dialogue for the current revision tree.
|
||||
|
||||
## Example device scenarios
|
||||
|
||||
### A user edits the branch shown on one device
|
||||
|
||||
Reference in New Issue
Block a user