mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-16 20:41:18 +00:00
## 0.24.26
### New Features - Automatic display-language changing according to the Obsidian language setting. - Now we can limit files to be synchronised even in the hidden files. - "Use Request API to avoid `inevitable` CORS problem" has been implemented. - `Show status icon instead of file warnings banner` has been implemented. ### Improved - All regular expressions can be inverted by prefixing `!!` now. ### Fixed - No longer unexpected files will be gathered during hidden file sync. - No longer broken `\n` and new-line characters during the bucket synchronisation. - We can purge the remote bucket again if we using MinIO instead of AWS S3 or Cloudflare R2. - Purging the remote bucket is now more reliable. - Some wrong messages have been fixed. ### Behaviour changed - Entering into the deeper directories to gather the hidden files is now limited by `/` or `\/` prefixed ignore filters. ### Etcetera - Some code has been tidied up. - Trying less warning-suppressing and be more safer-coding. - Dependent libraries have been updated to the latest version. - Some build processes have been separated to `pre` and `post` processes.
This commit is contained in:
@@ -1,31 +1,28 @@
|
||||
<script lang="ts">
|
||||
export let patterns = [] as string[];
|
||||
export let originals = [] as string[];
|
||||
import type { CustomRegExpSource } from "../../../lib/src/common/types";
|
||||
import { isInvertedRegExp, isValidRegExp } from "../../../lib/src/common/utils";
|
||||
|
||||
export let apply: (args: string[]) => Promise<void> = (_: string[]) => Promise.resolve();
|
||||
export let patterns = [] as CustomRegExpSource[];
|
||||
export let originals = [] as CustomRegExpSource[];
|
||||
|
||||
export let apply: (args: CustomRegExpSource[]) => Promise<void> = (_: CustomRegExpSource[]) => Promise.resolve();
|
||||
function revert() {
|
||||
patterns = [...originals];
|
||||
}
|
||||
const CHECK_OK = "✔";
|
||||
const CHECK_NG = "⚠";
|
||||
const MARK_MODIFIED = "✏ ";
|
||||
function checkRegExp(pattern: string) {
|
||||
if (pattern.trim() == "") return "";
|
||||
try {
|
||||
new RegExp(pattern);
|
||||
return CHECK_OK;
|
||||
} catch (ex) {
|
||||
return CHECK_NG;
|
||||
}
|
||||
function checkRegExp(pattern: CustomRegExpSource) {
|
||||
return isValidRegExp(pattern) ? CHECK_OK : CHECK_NG;
|
||||
}
|
||||
$: statusName = patterns.map((e) => checkRegExp(e));
|
||||
$: modified = patterns.map((e, i) => (e != (originals?.[i] ?? "") ? MARK_MODIFIED : ""));
|
||||
|
||||
$: isInvertedExp = patterns.map((e) => isInvertedRegExp(e));
|
||||
function remove(idx: number) {
|
||||
patterns[idx] = "";
|
||||
patterns[idx] = "" as CustomRegExpSource;
|
||||
}
|
||||
function add() {
|
||||
patterns = [...patterns, ""];
|
||||
patterns = [...patterns, "" as CustomRegExpSource];
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -33,7 +30,9 @@
|
||||
{#each patterns as pattern, idx}
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<li>
|
||||
<label>{modified[idx]}{statusName[idx]}</label><input type="text" bind:value={pattern} class={modified[idx]} />
|
||||
<label>{modified[idx]}{statusName[idx]}</label>
|
||||
<span class="chip">{isInvertedExp[idx] ? "INVERTED" : ""}</span>
|
||||
<input type="text" bind:value={pattern} class={modified[idx]} />
|
||||
<button class="iconbutton" on:click={() => remove(idx)}>🗑</button>
|
||||
</li>
|
||||
{/each}
|
||||
@@ -43,8 +42,16 @@
|
||||
</label>
|
||||
</li>
|
||||
<li class="buttons">
|
||||
<button on:click={() => apply(patterns)} disabled={statusName.some((e) => e === CHECK_NG) || modified.every((e) => e === "")}>Apply </button>
|
||||
<button on:click={() => revert()} disabled={statusName.some((e) => e === CHECK_NG) || modified.every((e) => e === "")}>Revert </button>
|
||||
<button
|
||||
on:click={() => apply(patterns)}
|
||||
disabled={statusName.some((e) => e === CHECK_NG) || modified.every((e) => e === "")}
|
||||
>Apply
|
||||
</button>
|
||||
<button
|
||||
on:click={() => revert()}
|
||||
disabled={statusName.some((e) => e === CHECK_NG) || modified.every((e) => e === "")}
|
||||
>Revert
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -85,4 +92,14 @@
|
||||
button.iconbutton {
|
||||
max-width: 4em;
|
||||
}
|
||||
.chip {
|
||||
background-color: var(--tag-background);
|
||||
color: var(--tag-color);
|
||||
padding: var(--size-2-1) var(--size-4-1);
|
||||
border-radius: 0.5em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.chip:empty {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -26,15 +26,19 @@ import {
|
||||
type MetaEntry,
|
||||
type FilePath,
|
||||
REMOTE_P2P,
|
||||
type CustomRegExpSource,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import {
|
||||
constructCustomRegExpList,
|
||||
createBlob,
|
||||
delay,
|
||||
getFileRegExp,
|
||||
isDocContentSame,
|
||||
isObjectDifferent,
|
||||
parseHeaderValues,
|
||||
readAsBlob,
|
||||
sizeToHumanReadable,
|
||||
splitCustomRegExpList,
|
||||
} from "../../../lib/src/common/utils.ts";
|
||||
import { arrayBufferToBase64Single, versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||
@@ -1132,7 +1136,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
(paneEl) => {
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleAppearance")).then((paneEl) => {
|
||||
const languages = Object.fromEntries([
|
||||
["", $msg("obsidianLiveSyncSettingTab.defaultLanguage")],
|
||||
// ["", $msg("obsidianLiveSyncSettingTab.defaultLanguage")],
|
||||
...SUPPORTED_I18N_LANGS.map((e) => [e, $t(`lang-${e}`)]),
|
||||
]) as Record<I18N_LANGS, string>;
|
||||
new Setting(paneEl).autoWireDropDown("displayLanguage", {
|
||||
@@ -1144,6 +1148,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
onUpdate: visibleOnly(() => this.isConfiguredAs("showStatusOnEditor", true)),
|
||||
});
|
||||
new Setting(paneEl).autoWireToggle("showStatusOnStatusbar");
|
||||
new Setting(paneEl).autoWireToggle("hideFileWarningNotice");
|
||||
});
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleLogging")).then((paneEl) => {
|
||||
paneEl.addClass("wizardHidden");
|
||||
@@ -2218,13 +2223,10 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
mount(MultipleRegExpControl, {
|
||||
target: syncFilesSetting.controlEl,
|
||||
props: {
|
||||
patterns: this.editingSettings.syncOnlyRegEx.split("|[]|"),
|
||||
originals: [...this.editingSettings.syncOnlyRegEx.split("|[]|")],
|
||||
apply: async (newPatterns: string[]) => {
|
||||
this.editingSettings.syncOnlyRegEx = newPatterns
|
||||
.map((e: string) => e.trim())
|
||||
.filter((e) => e != "")
|
||||
.join("|[]|");
|
||||
patterns: splitCustomRegExpList(this.editingSettings.syncOnlyRegEx, "|[]|"),
|
||||
originals: splitCustomRegExpList(this.editingSettings.syncOnlyRegEx, "|[]|"),
|
||||
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||
this.editingSettings.syncOnlyRegEx = constructCustomRegExpList(newPatterns, "|[]|");
|
||||
await this.saveAllDirtySettings();
|
||||
this.display();
|
||||
},
|
||||
@@ -2241,13 +2243,10 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
mount(MultipleRegExpControl, {
|
||||
target: nonSyncFilesSetting.controlEl,
|
||||
props: {
|
||||
patterns: this.editingSettings.syncIgnoreRegEx.split("|[]|"),
|
||||
originals: [...this.editingSettings.syncIgnoreRegEx.split("|[]|")],
|
||||
apply: async (newPatterns: string[]) => {
|
||||
this.editingSettings.syncIgnoreRegEx = newPatterns
|
||||
.map((e) => e.trim())
|
||||
.filter((e) => e != "")
|
||||
.join("|[]|");
|
||||
patterns: splitCustomRegExpList(this.editingSettings.syncIgnoreRegEx, "|[]|"),
|
||||
originals: splitCustomRegExpList(this.editingSettings.syncIgnoreRegEx, "|[]|"),
|
||||
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||
this.editingSettings.syncIgnoreRegEx = constructCustomRegExpList(newPatterns, "|[]|");
|
||||
await this.saveAllDirtySettings();
|
||||
this.display();
|
||||
},
|
||||
@@ -2261,14 +2260,32 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
});
|
||||
});
|
||||
void addPanel(paneEl, "Hidden Files", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => {
|
||||
const targetPatternSetting = new Setting(paneEl)
|
||||
.setName("Target patterns")
|
||||
.setClass("wizardHidden")
|
||||
.setDesc("Patterns to match files for syncing");
|
||||
const patTarget = splitCustomRegExpList(this.editingSettings.syncInternalFilesTargetPatterns, ",");
|
||||
mount(MultipleRegExpControl, {
|
||||
target: targetPatternSetting.controlEl,
|
||||
props: {
|
||||
patterns: patTarget,
|
||||
originals: [...patTarget],
|
||||
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||
this.editingSettings.syncInternalFilesTargetPatterns = constructCustomRegExpList(
|
||||
newPatterns,
|
||||
","
|
||||
);
|
||||
await this.saveAllDirtySettings();
|
||||
this.display();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const defaultSkipPattern = "\\/node_modules\\/, \\/\\.git\\/, ^\\.git\\/, \\/obsidian-livesync\\/";
|
||||
const defaultSkipPatternXPlat =
|
||||
defaultSkipPattern + ",\\/workspace$ ,\\/workspace.json$,\\/workspace-mobile.json$";
|
||||
|
||||
const pat = this.editingSettings.syncInternalFilesIgnorePatterns
|
||||
.split(",")
|
||||
.map((x) => x.trim())
|
||||
.filter((x) => x != "");
|
||||
const pat = splitCustomRegExpList(this.editingSettings.syncInternalFilesIgnorePatterns, ",");
|
||||
const patSetting = new Setting(paneEl).setName("Ignore patterns").setClass("wizardHidden").setDesc("");
|
||||
|
||||
mount(MultipleRegExpControl, {
|
||||
@@ -2276,11 +2293,11 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
props: {
|
||||
patterns: pat,
|
||||
originals: [...pat],
|
||||
apply: async (newPatterns: string[]) => {
|
||||
this.editingSettings.syncInternalFilesIgnorePatterns = newPatterns
|
||||
.map((e) => e.trim())
|
||||
.filter((e) => e != "")
|
||||
.join(", ");
|
||||
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||
this.editingSettings.syncInternalFilesIgnorePatterns = constructCustomRegExpList(
|
||||
newPatterns,
|
||||
","
|
||||
);
|
||||
await this.saveAllDirtySettings();
|
||||
this.display();
|
||||
},
|
||||
@@ -2288,16 +2305,13 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
});
|
||||
|
||||
const addDefaultPatterns = async (patterns: string) => {
|
||||
const oldList = this.editingSettings.syncInternalFilesIgnorePatterns
|
||||
.split(",")
|
||||
.map((x) => x.trim())
|
||||
.filter((x) => x != "");
|
||||
const newList = patterns
|
||||
.split(",")
|
||||
.map((x) => x.trim())
|
||||
.filter((x) => x != "");
|
||||
const allSet = new Set([...oldList, ...newList]);
|
||||
this.editingSettings.syncInternalFilesIgnorePatterns = [...allSet].join(", ");
|
||||
const oldList = splitCustomRegExpList(this.editingSettings.syncInternalFilesIgnorePatterns, ",");
|
||||
const newList = splitCustomRegExpList(
|
||||
patterns as unknown as typeof this.editingSettings.syncInternalFilesIgnorePatterns,
|
||||
","
|
||||
);
|
||||
const allSet = new Set<CustomRegExpSource>([...oldList, ...newList]);
|
||||
this.editingSettings.syncInternalFilesIgnorePatterns = constructCustomRegExpList([...allSet], ",");
|
||||
await this.saveAllDirtySettings();
|
||||
this.display();
|
||||
};
|
||||
@@ -2691,17 +2705,20 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
||||
const ignorePatterns = this.plugin.settings.syncInternalFilesIgnorePatterns
|
||||
.replace(/\n| /g, "")
|
||||
.split(",")
|
||||
.filter((e) => e)
|
||||
.map((e) => new RegExp(e, "i"));
|
||||
const ignorePatterns = getFileRegExp(
|
||||
this.plugin.settings,
|
||||
"syncInternalFilesIgnorePatterns"
|
||||
);
|
||||
const targetPatterns = getFileRegExp(
|
||||
this.plugin.settings,
|
||||
"syncInternalFilesTargetPatterns"
|
||||
);
|
||||
this.plugin.localDatabase.hashCaches.clear();
|
||||
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
||||
const files = this.plugin.settings.syncInternalFiles
|
||||
? await this.plugin.storageAccess.getFilesIncludeHidden(
|
||||
"/",
|
||||
undefined,
|
||||
targetPatterns,
|
||||
ignorePatterns
|
||||
)
|
||||
: await this.plugin.storageAccess.getFileNames();
|
||||
@@ -2721,7 +2738,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
i++;
|
||||
if (i % 25 == 0)
|
||||
Logger(
|
||||
`Checking ${i}/${files.length} files \n`,
|
||||
`Checking ${i}/${allPaths.length} files \n`,
|
||||
LOG_LEVEL_NOTICE,
|
||||
"verify-processed"
|
||||
);
|
||||
@@ -3087,7 +3104,9 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
onUpdate: visibleOnly(() => this.isConfiguredAs("disableWorkerForGeneratingChunks", false)),
|
||||
});
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Edge case addressing (Networking)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("useRequestAPI");
|
||||
});
|
||||
void addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch");
|
||||
});
|
||||
|
||||
@@ -377,6 +377,14 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
||||
name: "Minimum interval for syncing",
|
||||
desc: "The minimum interval for automatic synchronisation on event.",
|
||||
},
|
||||
useRequestAPI: {
|
||||
name: "Use Request API to avoid `inevitable` CORS problem",
|
||||
desc: "If enabled, the request API will be used to avoid `inevitable` CORS problems. This is a workaround and may not work in all cases. PLEASE READ THE DOCUMENTATION BEFORE USING THIS OPTION. This is a less-secure option.",
|
||||
},
|
||||
hideFileWarningNotice: {
|
||||
name: "Show status icon instead of file warnings banner",
|
||||
desc: "If enabled, the ⛔ icon will be shown inside the status instead of the file warnings banner. No details will be shown.",
|
||||
},
|
||||
};
|
||||
function translateInfo(infoSrc: ConfigurationItem | undefined | false) {
|
||||
if (!infoSrc) return false;
|
||||
|
||||
Reference in New Issue
Block a user