mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-12-15 18:55:57 +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:
4819
package-lock.json
generated
4819
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
44
package.json
44
package.json
@@ -6,8 +6,10 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bakei18n": "npx tsx ./src/lib/_tools/bakei18n.ts",
|
"bakei18n": "npx tsx ./src/lib/_tools/bakei18n.ts",
|
||||||
|
"postbakei18n": "prettier --config ./.prettierrc ./src/lib/src/common/messages/*.ts --write --log-level error",
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "npm run bakei18n && node esbuild.config.mjs production",
|
"prebuild": "npm run bakei18n",
|
||||||
|
"build": "node esbuild.config.mjs production",
|
||||||
"buildDev": "node esbuild.config.mjs dev",
|
"buildDev": "node esbuild.config.mjs dev",
|
||||||
"lint": "eslint src",
|
"lint": "eslint src",
|
||||||
"svelte-check": "svelte-check --tsconfig ./tsconfig.json",
|
"svelte-check": "svelte-check --tsconfig ./tsconfig.json",
|
||||||
@@ -22,12 +24,12 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chialab/esbuild-plugin-worker": "^0.18.1",
|
"@chialab/esbuild-plugin-worker": "^0.18.1",
|
||||||
"@eslint/compat": "^1.2.6",
|
"@eslint/compat": "^1.2.7",
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.3.0",
|
||||||
"@eslint/js": "^9.20.0",
|
"@eslint/js": "^9.21.0",
|
||||||
"@tsconfig/svelte": "^5.0.4",
|
"@tsconfig/svelte": "^5.0.4",
|
||||||
"@types/diff-match-patch": "^1.0.36",
|
"@types/diff-match-patch": "^1.0.36",
|
||||||
"@types/node": "^22.5.4",
|
"@types/node": "^22.13.8",
|
||||||
"@types/pouchdb": "^6.4.2",
|
"@types/pouchdb": "^6.4.2",
|
||||||
"@types/pouchdb-adapter-http": "^6.1.6",
|
"@types/pouchdb-adapter-http": "^6.1.6",
|
||||||
"@types/pouchdb-adapter-idb": "^6.1.7",
|
"@types/pouchdb-adapter-idb": "^6.1.7",
|
||||||
@@ -36,17 +38,17 @@
|
|||||||
"@types/pouchdb-mapreduce": "^6.1.10",
|
"@types/pouchdb-mapreduce": "^6.1.10",
|
||||||
"@types/pouchdb-replication": "^6.4.7",
|
"@types/pouchdb-replication": "^6.4.7",
|
||||||
"@types/transform-pouch": "^1.0.6",
|
"@types/transform-pouch": "^1.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.24.1",
|
"@typescript-eslint/eslint-plugin": "8.25.0",
|
||||||
"@typescript-eslint/parser": "^8.24.1",
|
"@typescript-eslint/parser": "8.25.0",
|
||||||
"builtin-modules": "^4.0.0",
|
"builtin-modules": "5.0.0",
|
||||||
"esbuild": "0.24.2",
|
"esbuild": "0.25.0",
|
||||||
"esbuild-svelte": "^0.9.0",
|
"esbuild-svelte": "^0.9.0",
|
||||||
"eslint": "^9.20.1",
|
"eslint": "^9.21.0",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-svelte": "^2.46.1",
|
"eslint-plugin-svelte": "^3.0.2",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"obsidian": "^1.7.2",
|
"obsidian": "^1.8.7",
|
||||||
"postcss": "^8.5.2",
|
"postcss": "^8.5.3",
|
||||||
"postcss-load-config": "^6.0.1",
|
"postcss-load-config": "^6.0.1",
|
||||||
"pouchdb-adapter-http": "^9.0.0",
|
"pouchdb-adapter-http": "^9.0.0",
|
||||||
"pouchdb-adapter-idb": "^9.0.0",
|
"pouchdb-adapter-idb": "^9.0.0",
|
||||||
@@ -58,28 +60,30 @@
|
|||||||
"pouchdb-merge": "^9.0.0",
|
"pouchdb-merge": "^9.0.0",
|
||||||
"pouchdb-replication": "^9.0.0",
|
"pouchdb-replication": "^9.0.0",
|
||||||
"pouchdb-utils": "^9.0.0",
|
"pouchdb-utils": "^9.0.0",
|
||||||
"prettier": "^3.5.1",
|
"prettier": "3.5.2",
|
||||||
"svelte": "^5.20.1",
|
"svelte": "5.28.6",
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"terser": "^5.39.0",
|
"terser": "^5.39.0",
|
||||||
"transform-pouch": "^2.0.0",
|
"transform-pouch": "^2.0.0",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"tsx": "^4.19.2",
|
"tsx": "^4.19.4",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "5.7.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.787.0",
|
"@aws-sdk/client-s3": "^3.808.0",
|
||||||
|
"@smithy/md5-js": "^4.0.2",
|
||||||
|
"@smithy/middleware-apply-body-checksum": "^4.1.0",
|
||||||
"@smithy/fetch-http-handler": "^5.0.2",
|
"@smithy/fetch-http-handler": "^5.0.2",
|
||||||
"@smithy/protocol-http": "^5.1.0",
|
"@smithy/protocol-http": "^5.1.0",
|
||||||
"@smithy/querystring-builder": "^4.0.2",
|
"@smithy/querystring-builder": "^4.0.2",
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"idb": "^8.0.2",
|
"idb": "^8.0.3",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
"octagonal-wheels": "^0.1.25",
|
"octagonal-wheels": "^0.1.25",
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
"svelte-check": "^4.1.4",
|
"svelte-check": "^4.1.7",
|
||||||
"trystero": "^0.21.3",
|
"trystero": "^0.21.3",
|
||||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import {
|
|||||||
readContent,
|
readContent,
|
||||||
createBlob,
|
createBlob,
|
||||||
fireAndForget,
|
fireAndForget,
|
||||||
|
type CustomRegExp,
|
||||||
|
getFileRegExp,
|
||||||
} from "../../lib/src/common/utils.ts";
|
} from "../../lib/src/common/utils.ts";
|
||||||
import {
|
import {
|
||||||
compareMTime,
|
compareMTime,
|
||||||
@@ -164,12 +166,11 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
updateSettingCache() {
|
updateSettingCache() {
|
||||||
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns
|
const ignorePatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns");
|
||||||
.replace(/\n| /g, "")
|
|
||||||
.split(",")
|
|
||||||
.filter((e) => e)
|
|
||||||
.map((e) => new RegExp(e, "i"));
|
|
||||||
this.ignorePatterns = ignorePatterns;
|
this.ignorePatterns = ignorePatterns;
|
||||||
|
const targetFilter = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns");
|
||||||
|
this.targetPatterns = targetFilter;
|
||||||
|
|
||||||
this.shouldSkipFile = [] as FilePathWithPrefixLC[];
|
this.shouldSkipFile = [] as FilePathWithPrefixLC[];
|
||||||
// Exclude files handled by customization sync
|
// Exclude files handled by customization sync
|
||||||
const configDir = normalizePath(this.app.vault.configDir);
|
const configDir = normalizePath(this.app.vault.configDir);
|
||||||
@@ -219,12 +220,10 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
? this.settings.syncInternalFilesInterval * 1000
|
? this.settings.syncInternalFilesInterval * 1000
|
||||||
: 0
|
: 0
|
||||||
);
|
);
|
||||||
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns
|
const ignorePatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns");
|
||||||
.replace(/\n| /g, "")
|
|
||||||
.split(",")
|
|
||||||
.filter((e) => e)
|
|
||||||
.map((e) => new RegExp(e, "i"));
|
|
||||||
this.ignorePatterns = ignorePatterns;
|
this.ignorePatterns = ignorePatterns;
|
||||||
|
const targetFilter = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns");
|
||||||
|
this.targetPatterns = targetFilter;
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1683,14 +1682,12 @@ ${messageFetch}${messageOverwrite}${messageMerge}
|
|||||||
// <-- Configuration handling
|
// <-- Configuration handling
|
||||||
|
|
||||||
// --> Local Storage SubFunctions
|
// --> Local Storage SubFunctions
|
||||||
ignorePatterns: RegExp[] = [];
|
ignorePatterns: CustomRegExp[] = [];
|
||||||
|
targetPatterns: CustomRegExp[] = [];
|
||||||
async scanInternalFileNames() {
|
async scanInternalFileNames() {
|
||||||
const configDir = normalizePath(this.app.vault.configDir);
|
const configDir = normalizePath(this.app.vault.configDir);
|
||||||
const ignoreFilter = this.settings.syncInternalFilesIgnorePatterns
|
const ignoreFilter = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns");
|
||||||
.replace(/\n| /g, "")
|
const targetFilter = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns");
|
||||||
.split(",")
|
|
||||||
.filter((e) => e)
|
|
||||||
.map((e) => new RegExp(e, "i"));
|
|
||||||
const synchronisedInConfigSync = !this.settings.usePluginSync
|
const synchronisedInConfigSync = !this.settings.usePluginSync
|
||||||
? []
|
? []
|
||||||
: Object.values(this.settings.pluginSyncExtendedSetting)
|
: Object.values(this.settings.pluginSyncExtendedSetting)
|
||||||
@@ -1701,7 +1698,7 @@ ${messageFetch}${messageOverwrite}${messageMerge}
|
|||||||
const root = this.app.vault.getRoot();
|
const root = this.app.vault.getRoot();
|
||||||
const findRoot = root.path;
|
const findRoot = root.path;
|
||||||
|
|
||||||
const filenames = (await this.getFiles(findRoot, [], undefined, ignoreFilter))
|
const filenames = (await this.getFiles(findRoot, [], targetFilter, ignoreFilter))
|
||||||
.filter((e) => e.startsWith("."))
|
.filter((e) => e.startsWith("."))
|
||||||
.filter((e) => !e.startsWith(".trash"));
|
.filter((e) => !e.startsWith(".trash"));
|
||||||
const files = filenames.filter((path) =>
|
const files = filenames.filter((path) =>
|
||||||
@@ -1737,7 +1734,7 @@ ${messageFetch}${messageOverwrite}${messageMerge}
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFiles(path: string, ignoreList: string[], filter?: RegExp[], ignoreFilter?: RegExp[]) {
|
async getFiles(path: string, ignoreList: string[], filter?: CustomRegExp[], ignoreFilter?: CustomRegExp[]) {
|
||||||
let w: ListedFiles;
|
let w: ListedFiles;
|
||||||
try {
|
try {
|
||||||
w = await this.app.vault.adapter.list(path);
|
w = await this.app.vault.adapter.list(path);
|
||||||
@@ -1746,26 +1743,32 @@ ${messageFetch}${messageOverwrite}${messageMerge}
|
|||||||
this._log(ex, LOG_LEVEL_VERBOSE);
|
this._log(ex, LOG_LEVEL_VERBOSE);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const filesSrc = [
|
|
||||||
...w.files
|
|
||||||
.filter((e) => !ignoreList.some((ee) => e.endsWith(ee)))
|
|
||||||
.filter((e) => !filter || filter.some((ee) => e.match(ee)))
|
|
||||||
.filter((e) => !ignoreFilter || ignoreFilter.every((ee) => !e.match(ee))),
|
|
||||||
];
|
|
||||||
let files = [] as string[];
|
let files = [] as string[];
|
||||||
for (const file of filesSrc) {
|
for (const file of w.files) {
|
||||||
if (!(await this.plugin.$$isIgnoredByIgnoreFiles(file))) {
|
if (ignoreList && ignoreList.length > 0) {
|
||||||
files.push(file);
|
if (ignoreList.some((e) => file.endsWith(e))) continue;
|
||||||
}
|
}
|
||||||
|
if (filter && filter.length > 0) {
|
||||||
|
if (!filter.some((e) => e.test(file))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ignoreFilter && ignoreFilter.some((ee) => ee.test(file))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue;
|
||||||
|
files.push(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
L1: for (const v of w.folders) {
|
L1: for (const v of w.folders) {
|
||||||
for (const ignore of ignoreList) {
|
for (const ignore of ignoreList) {
|
||||||
if (v.endsWith(ignore)) {
|
if (v.endsWith(ignore)) {
|
||||||
continue L1;
|
continue L1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ignoreFilter && ignoreFilter.some((e) => v.match(e))) {
|
if (
|
||||||
|
ignoreFilter &&
|
||||||
|
ignoreFilter.some((e) => (e.pattern.startsWith("/") || e.pattern.startsWith("\\/")) && e.test(v))
|
||||||
|
) {
|
||||||
continue L1;
|
continue L1;
|
||||||
}
|
}
|
||||||
if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) {
|
if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) {
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: c8bb4fedbb...d53cad1c68
@@ -291,7 +291,8 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
performSetup: boolean,
|
performSetup: boolean,
|
||||||
skipInfo: boolean,
|
skipInfo: boolean,
|
||||||
compression: boolean,
|
compression: boolean,
|
||||||
customHeaders: Record<string, string>
|
customHeaders: Record<string, string>,
|
||||||
|
useRequestAPI: boolean
|
||||||
): Promise<
|
): Promise<
|
||||||
| string
|
| string
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type {
|
|||||||
import { TFileToUXFileInfoStub, TFolderToUXFileInfoStub } from "./storageLib/utilObsidian.ts";
|
import { TFileToUXFileInfoStub, TFolderToUXFileInfoStub } from "./storageLib/utilObsidian.ts";
|
||||||
import { StorageEventManagerObsidian, type StorageEventManager } from "./storageLib/StorageEventManager";
|
import { StorageEventManagerObsidian, type StorageEventManager } from "./storageLib/StorageEventManager";
|
||||||
import type { StorageAccess } from "../interfaces/StorageAccess";
|
import type { StorageAccess } from "../interfaces/StorageAccess";
|
||||||
import { createBlob } from "../../lib/src/common/utils";
|
import { createBlob, type CustomRegExp } from "../../lib/src/common/utils";
|
||||||
|
|
||||||
export class ModuleFileAccessObsidian extends AbstractObsidianModule implements IObsidianModule, StorageAccess {
|
export class ModuleFileAccessObsidian extends AbstractObsidianModule implements IObsidianModule, StorageAccess {
|
||||||
vaultAccess!: SerializedFileAccess;
|
vaultAccess!: SerializedFileAccess;
|
||||||
@@ -223,8 +223,8 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
|||||||
|
|
||||||
async getFilesIncludeHidden(
|
async getFilesIncludeHidden(
|
||||||
basePath: string,
|
basePath: string,
|
||||||
includeFilter?: RegExp[],
|
includeFilter?: CustomRegExp[],
|
||||||
excludeFilter?: RegExp[],
|
excludeFilter?: CustomRegExp[],
|
||||||
skipFolder: string[] = [".git", ".trash", "node_modules"]
|
skipFolder: string[] = [".git", ".trash", "node_modules"]
|
||||||
): Promise<FilePath[]> {
|
): Promise<FilePath[]> {
|
||||||
let w: ListedFiles;
|
let w: ListedFiles;
|
||||||
@@ -239,16 +239,11 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
|||||||
|
|
||||||
let files = [] as string[];
|
let files = [] as string[];
|
||||||
for (const file of w.files) {
|
for (const file of w.files) {
|
||||||
if (excludeFilter && excludeFilter.some((ee) => file.match(ee))) {
|
if (includeFilter && includeFilter.length > 0) {
|
||||||
// If excludeFilter and includeFilter are both set, the file will be included in the list.
|
if (!includeFilter.some((e) => e.test(file))) continue;
|
||||||
if (includeFilter) {
|
|
||||||
if (!includeFilter.some((e) => file.match(e))) continue;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (includeFilter) {
|
if (excludeFilter && excludeFilter.some((ee) => ee.test(file))) {
|
||||||
if (!includeFilter.some((e) => file.match(e))) continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue;
|
if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue;
|
||||||
files.push(file);
|
files.push(file);
|
||||||
@@ -259,16 +254,12 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
|||||||
if (skipFolder.some((e) => folderName === e)) {
|
if (skipFolder.some((e) => folderName === e)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (excludeFilter && excludeFilter.some((e) => v.match(e))) {
|
|
||||||
if (includeFilter) {
|
|
||||||
if (!includeFilter.some((e) => v.match(e))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeFilter) {
|
if (excludeFilter && excludeFilter.some((e) => e.test(v))) {
|
||||||
if (!includeFilter.some((e) => v.match(e))) continue;
|
continue;
|
||||||
|
}
|
||||||
|
if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
// OK, deep dive!
|
// OK, deep dive!
|
||||||
files = files.concat(await this.getFilesIncludeHidden(v, includeFilter, excludeFilter, skipFolder));
|
files = files.concat(await this.getFilesIncludeHidden(v, includeFilter, excludeFilter, skipFolder));
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
type UXFileInfoStub,
|
type UXFileInfoStub,
|
||||||
type UXInternalFileInfoStub,
|
type UXInternalFileInfoStub,
|
||||||
} from "../../../lib/src/common/types.ts";
|
} from "../../../lib/src/common/types.ts";
|
||||||
import { delay, fireAndForget } from "../../../lib/src/common/utils.ts";
|
import { delay, fireAndForget, getFileRegExp } from "../../../lib/src/common/utils.ts";
|
||||||
import { type FileEventItem, type FileEventType } from "../../../common/types.ts";
|
import { type FileEventItem, type FileEventType } from "../../../common/types.ts";
|
||||||
import { serialized, skipIfDuplicated } from "../../../lib/src/concurrency/lock.ts";
|
import { serialized, skipIfDuplicated } from "../../../lib/src/concurrency/lock.ts";
|
||||||
import {
|
import {
|
||||||
@@ -161,12 +161,10 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
if (!this.plugin.settings.syncInternalFiles && !this.plugin.settings.usePluginSync) return;
|
if (!this.plugin.settings.syncInternalFiles && !this.plugin.settings.usePluginSync) return;
|
||||||
if (!this.plugin.settings.watchInternalFileChanges) return;
|
if (!this.plugin.settings.watchInternalFileChanges) return;
|
||||||
if (!path.startsWith(this.plugin.app.vault.configDir)) return;
|
if (!path.startsWith(this.plugin.app.vault.configDir)) return;
|
||||||
const ignorePatterns = this.plugin.settings.syncInternalFilesIgnorePatterns
|
const ignorePatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns");
|
||||||
.replace(/\n| /g, "")
|
const targetPatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns");
|
||||||
.split(",")
|
if (ignorePatterns.some((e) => e.test(path))) return;
|
||||||
.filter((e) => e)
|
if (!targetPatterns.some((e) => e.test(path))) return;
|
||||||
.map((e) => new RegExp(e, "i"));
|
|
||||||
if (ignorePatterns.some((e) => path.match(e))) return;
|
|
||||||
if (path.endsWith("/")) {
|
if (path.endsWith("/")) {
|
||||||
// Folder
|
// Folder
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -17,17 +17,8 @@ import { AuthorizationHeaderGenerator } from "../../lib/src/replication/httplib.
|
|||||||
|
|
||||||
setNoticeClass(Notice);
|
setNoticeClass(Notice);
|
||||||
|
|
||||||
async function fetchByAPI(request: RequestUrlParam): Promise<RequestUrlResponse> {
|
async function fetchByAPI(request: RequestUrlParam, errorAsResult = false): Promise<RequestUrlResponse> {
|
||||||
const ret = await requestUrl(request);
|
const ret = await requestUrl({ ...request, throw: !errorAsResult });
|
||||||
if (ret.status - (ret.status % 100) !== 200) {
|
|
||||||
const er: Error & { status?: number } = new Error(`Request Error:${ret.status}`);
|
|
||||||
if (ret.json) {
|
|
||||||
er.message = ret.json.reason;
|
|
||||||
er.name = `${ret.json.error ?? ""}:${ret.json.message ?? ""}`;
|
|
||||||
}
|
|
||||||
er.status = ret.status;
|
|
||||||
throw er;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,13 +36,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
return !this.last_successful_post;
|
return !this.last_successful_post;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchByAPI(
|
async _fetchByAPI(url: string, authHeader: string, opts?: RequestInit): Promise<Response> {
|
||||||
url: string,
|
|
||||||
localURL: string,
|
|
||||||
method: string,
|
|
||||||
authHeader: string,
|
|
||||||
opts?: RequestInit
|
|
||||||
): Promise<Response> {
|
|
||||||
const body = opts?.body as string;
|
const body = opts?.body as string;
|
||||||
|
|
||||||
const transformedHeaders = { ...(opts?.headers as Record<string, string>) };
|
const transformedHeaders = { ...(opts?.headers as Record<string, string>) };
|
||||||
@@ -65,25 +50,36 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
method: opts?.method,
|
method: opts?.method,
|
||||||
body: body,
|
body: body,
|
||||||
headers: transformedHeaders,
|
headers: transformedHeaders,
|
||||||
contentType: "application/json",
|
contentType:
|
||||||
// contentType: opts.headers,
|
transformedHeaders?.["content-type"] ?? transformedHeaders?.["Content-Type"] ?? "application/json",
|
||||||
};
|
};
|
||||||
|
const r = await fetchByAPI(requestParam, true);
|
||||||
|
return new Response(r.arrayBuffer, {
|
||||||
|
headers: r.headers,
|
||||||
|
status: r.status,
|
||||||
|
statusText: `${r.status}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchByAPI(
|
||||||
|
url: string,
|
||||||
|
localURL: string,
|
||||||
|
method: string,
|
||||||
|
authHeader: string,
|
||||||
|
opts?: RequestInit
|
||||||
|
): Promise<Response> {
|
||||||
|
const body = opts?.body as string;
|
||||||
const size = body ? ` (${body.length})` : "";
|
const size = body ? ` (${body.length})` : "";
|
||||||
try {
|
try {
|
||||||
|
const r = await this._fetchByAPI(url, authHeader, opts);
|
||||||
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
||||||
const r = await fetchByAPI(requestParam);
|
|
||||||
if (method == "POST" || method == "PUT") {
|
if (method == "POST" || method == "PUT") {
|
||||||
this.last_successful_post = r.status - (r.status % 100) == 200;
|
this.last_successful_post = r.status - (r.status % 100) == 200;
|
||||||
} else {
|
} else {
|
||||||
this.last_successful_post = true;
|
this.last_successful_post = true;
|
||||||
}
|
}
|
||||||
this._log(`HTTP:${method}${size} to:${localURL} -> ${r.status}`, LOG_LEVEL_DEBUG);
|
this._log(`HTTP:${method}${size} to:${localURL} -> ${r.status}`, LOG_LEVEL_DEBUG);
|
||||||
|
return r;
|
||||||
return new Response(r.arrayBuffer, {
|
|
||||||
headers: r.headers,
|
|
||||||
status: r.status,
|
|
||||||
statusText: `${r.status}`,
|
|
||||||
});
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
|
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
|
||||||
// limit only in bulk_docs.
|
// limit only in bulk_docs.
|
||||||
@@ -106,7 +102,8 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
performSetup: boolean,
|
performSetup: boolean,
|
||||||
skipInfo: boolean,
|
skipInfo: boolean,
|
||||||
compression: boolean,
|
compression: boolean,
|
||||||
customHeaders: Record<string, string>
|
customHeaders: Record<string, string>,
|
||||||
|
useRequestAPI: boolean
|
||||||
): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
||||||
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
||||||
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
||||||
@@ -147,15 +144,11 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
headers.append("authorization", authHeader);
|
headers.append("authorization", authHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!disableRequestURI && typeof url == "string" && typeof (opts?.body ?? "") == "string") {
|
|
||||||
// Deprecated configuration, only for backward compatibility.
|
|
||||||
return await this.fetchByAPI(url, localURL, method, authHeader, { ...opts, headers });
|
|
||||||
}
|
|
||||||
// --> native Fetch API.
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
||||||
const response: Response = await fetch(url, { ...opts, headers });
|
const response: Response = await (useRequestAPI
|
||||||
|
? this._fetchByAPI(url.toString(), authHeader, { ...opts, headers })
|
||||||
|
: fetch(url, { ...opts, headers }));
|
||||||
if (method == "POST" || method == "PUT") {
|
if (method == "POST" || method == "PUT") {
|
||||||
this.last_successful_post = response.ok;
|
this.last_successful_post = response.ok;
|
||||||
} else {
|
} else {
|
||||||
@@ -188,6 +181,10 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
return response;
|
return response;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex instanceof TypeError) {
|
if (ex instanceof TypeError) {
|
||||||
|
if (useRequestAPI) {
|
||||||
|
this._log("Failed to request by API.");
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
this._log(
|
this._log(
|
||||||
"Failed to fetch by native fetch API. Trying to fetch by API to get more information."
|
"Failed to fetch by native fetch API. Trying to fetch by API to get more information."
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { uint8ArrayToHexString } from "octagonal-wheels/binary/hex";
|
|||||||
import { parseYaml, requestUrl, stringifyYaml } from "obsidian";
|
import { parseYaml, requestUrl, stringifyYaml } from "obsidian";
|
||||||
import type { FilePath } from "../../lib/src/common/types.ts";
|
import type { FilePath } from "../../lib/src/common/types.ts";
|
||||||
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||||
|
import { getFileRegExp } from "../../lib/src/common/utils.ts";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface LSEvents {
|
interface LSEvents {
|
||||||
@@ -200,13 +201,10 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _dumpFileListIncludeHidden(outFile?: string) {
|
async _dumpFileListIncludeHidden(outFile?: string) {
|
||||||
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns
|
const ignorePatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns");
|
||||||
.replace(/\n| /g, "")
|
const targetPatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns");
|
||||||
.split(",")
|
|
||||||
.filter((e) => e)
|
|
||||||
.map((e) => new RegExp(e, "i"));
|
|
||||||
const out = [] as any[];
|
const out = [] as any[];
|
||||||
const files = await this.core.storageAccess.getFilesIncludeHidden("", undefined, ignorePatterns);
|
const files = await this.core.storageAccess.getFilesIncludeHidden("", targetPatterns, ignorePatterns);
|
||||||
// console.dir(files);
|
// console.dir(files);
|
||||||
const webcrypto = await getWebCrypto();
|
const webcrypto = await getWebCrypto();
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
|
|
||||||
statusBarLabels!: ReactiveValue<{ message: string; status: string }>;
|
statusBarLabels!: ReactiveValue<{ message: string; status: string }>;
|
||||||
statusLog = reactiveSource("");
|
statusLog = reactiveSource("");
|
||||||
|
activeFileStatus = reactiveSource("");
|
||||||
notifies: { [key: string]: { notice: Notice; count: number } } = {};
|
notifies: { [key: string]: { notice: Notice; count: number } } = {};
|
||||||
p2pLogCollector = new P2PLogCollector();
|
p2pLogCollector = new P2PLogCollector();
|
||||||
|
|
||||||
@@ -181,10 +182,11 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n`
|
? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n`
|
||||||
: "";
|
: "";
|
||||||
const { message } = statusLineLabel();
|
const { message } = statusLineLabel();
|
||||||
|
const fileStatus = this.activeFileStatus.value;
|
||||||
const status = scheduleMessage + this.statusLog.value;
|
const status = scheduleMessage + this.statusLog.value;
|
||||||
|
const fileStatusIcon = `${fileStatus && this.settings.hideFileWarningNotice ? " ⛔ SKIP" : ""}`;
|
||||||
return {
|
return {
|
||||||
message,
|
message: `${message}${fileStatusIcon}`,
|
||||||
status,
|
status,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -229,7 +231,9 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
async setFileStatus() {
|
async setFileStatus() {
|
||||||
this.messageArea!.innerText = await this.getActiveFileStatus();
|
const fileStatus = await this.getActiveFileStatus();
|
||||||
|
this.activeFileStatus.value = fileStatus;
|
||||||
|
this.messageArea!.innerText = this.settings.hideFileWarningNotice ? "" : fileStatus;
|
||||||
}
|
}
|
||||||
onActiveLeafChange() {
|
onActiveLeafChange() {
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
|
|||||||
@@ -11,9 +11,45 @@ import {
|
|||||||
} from "../../lib/src/common/types";
|
} from "../../lib/src/common/types";
|
||||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT } from "octagonal-wheels/common/logger";
|
import { LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT } from "octagonal-wheels/common/logger";
|
||||||
import { encrypt, tryDecrypt } from "octagonal-wheels/encryption";
|
import { encrypt, tryDecrypt } from "octagonal-wheels/encryption";
|
||||||
import { setLang } from "../../lib/src/common/i18n";
|
import { $msg, setLang } from "../../lib/src/common/i18n";
|
||||||
import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb";
|
import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb";
|
||||||
|
import { getLanguage } from "obsidian";
|
||||||
|
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts";
|
||||||
export class ModuleObsidianSettings extends AbstractObsidianModule implements IObsidianModule {
|
export class ModuleObsidianSettings extends AbstractObsidianModule implements IObsidianModule {
|
||||||
|
async $everyOnLayoutReady(): Promise<boolean> {
|
||||||
|
let isChanged = false;
|
||||||
|
if (this.settings.displayLanguage == "") {
|
||||||
|
const obsidianLanguage = getLanguage();
|
||||||
|
if (
|
||||||
|
SUPPORTED_I18N_LANGS.indexOf(obsidianLanguage) !== -1 && // Check if the language is supported
|
||||||
|
obsidianLanguage != this.settings.displayLanguage && // Check if the language is different from the current setting
|
||||||
|
this.settings.displayLanguage != ""
|
||||||
|
) {
|
||||||
|
// Check if the current setting is not empty (Means migrated or installed).
|
||||||
|
this.settings.displayLanguage = obsidianLanguage as I18N_LANGS;
|
||||||
|
isChanged = true;
|
||||||
|
setLang(this.settings.displayLanguage);
|
||||||
|
} else if (this.settings.displayLanguage == "") {
|
||||||
|
this.settings.displayLanguage = "def";
|
||||||
|
setLang(this.settings.displayLanguage);
|
||||||
|
await this.core.$$saveSettingData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isChanged) {
|
||||||
|
const revert = $msg("dialog.yourLanguageAvailable.btnRevertToDefault");
|
||||||
|
if (
|
||||||
|
(await this.core.confirm.askSelectStringDialogue($msg(`dialog.yourLanguageAvailable`), ["OK", revert], {
|
||||||
|
defaultAction: "OK",
|
||||||
|
title: $msg(`dialog.yourLanguageAvailable.Title`),
|
||||||
|
})) == revert
|
||||||
|
) {
|
||||||
|
this.settings.displayLanguage = "def";
|
||||||
|
setLang(this.settings.displayLanguage);
|
||||||
|
}
|
||||||
|
await this.core.$$saveSettingData();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
getPassphrase(settings: ObsidianLiveSyncSettings) {
|
getPassphrase(settings: ObsidianLiveSyncSettings) {
|
||||||
const methods: Record<ConfigPassphraseStore, () => Promise<string | false>> = {
|
const methods: Record<ConfigPassphraseStore, () => Promise<string | false>> = {
|
||||||
"": () => Promise.resolve("*"),
|
"": () => Promise.resolve("*"),
|
||||||
@@ -199,6 +235,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
setLang(this.settings.displayLanguage);
|
setLang(this.settings.displayLanguage);
|
||||||
|
|
||||||
if ("workingEncrypt" in this.settings) delete this.settings.workingEncrypt;
|
if ("workingEncrypt" in this.settings) delete this.settings.workingEncrypt;
|
||||||
|
|||||||
@@ -1,31 +1,28 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let patterns = [] as string[];
|
import type { CustomRegExpSource } from "../../../lib/src/common/types";
|
||||||
export let originals = [] as string[];
|
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() {
|
function revert() {
|
||||||
patterns = [...originals];
|
patterns = [...originals];
|
||||||
}
|
}
|
||||||
const CHECK_OK = "✔";
|
const CHECK_OK = "✔";
|
||||||
const CHECK_NG = "⚠";
|
const CHECK_NG = "⚠";
|
||||||
const MARK_MODIFIED = "✏ ";
|
const MARK_MODIFIED = "✏ ";
|
||||||
function checkRegExp(pattern: string) {
|
function checkRegExp(pattern: CustomRegExpSource) {
|
||||||
if (pattern.trim() == "") return "";
|
return isValidRegExp(pattern) ? CHECK_OK : CHECK_NG;
|
||||||
try {
|
|
||||||
new RegExp(pattern);
|
|
||||||
return CHECK_OK;
|
|
||||||
} catch (ex) {
|
|
||||||
return CHECK_NG;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$: statusName = patterns.map((e) => checkRegExp(e));
|
$: statusName = patterns.map((e) => checkRegExp(e));
|
||||||
$: modified = patterns.map((e, i) => (e != (originals?.[i] ?? "") ? MARK_MODIFIED : ""));
|
$: modified = patterns.map((e, i) => (e != (originals?.[i] ?? "") ? MARK_MODIFIED : ""));
|
||||||
|
$: isInvertedExp = patterns.map((e) => isInvertedRegExp(e));
|
||||||
function remove(idx: number) {
|
function remove(idx: number) {
|
||||||
patterns[idx] = "";
|
patterns[idx] = "" as CustomRegExpSource;
|
||||||
}
|
}
|
||||||
function add() {
|
function add() {
|
||||||
patterns = [...patterns, ""];
|
patterns = [...patterns, "" as CustomRegExpSource];
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -33,7 +30,9 @@
|
|||||||
{#each patterns as pattern, idx}
|
{#each patterns as pattern, idx}
|
||||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||||
<li>
|
<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>
|
<button class="iconbutton" on:click={() => remove(idx)}>🗑</button>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -43,8 +42,16 @@
|
|||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li class="buttons">
|
<li class="buttons">
|
||||||
<button on:click={() => apply(patterns)} disabled={statusName.some((e) => e === CHECK_NG) || modified.every((e) => e === "")}>Apply </button>
|
<button
|
||||||
<button on:click={() => revert()} disabled={statusName.some((e) => e === CHECK_NG) || modified.every((e) => e === "")}>Revert </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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@@ -85,4 +92,14 @@
|
|||||||
button.iconbutton {
|
button.iconbutton {
|
||||||
max-width: 4em;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -26,15 +26,19 @@ import {
|
|||||||
type MetaEntry,
|
type MetaEntry,
|
||||||
type FilePath,
|
type FilePath,
|
||||||
REMOTE_P2P,
|
REMOTE_P2P,
|
||||||
|
type CustomRegExpSource,
|
||||||
} from "../../../lib/src/common/types.ts";
|
} from "../../../lib/src/common/types.ts";
|
||||||
import {
|
import {
|
||||||
|
constructCustomRegExpList,
|
||||||
createBlob,
|
createBlob,
|
||||||
delay,
|
delay,
|
||||||
|
getFileRegExp,
|
||||||
isDocContentSame,
|
isDocContentSame,
|
||||||
isObjectDifferent,
|
isObjectDifferent,
|
||||||
parseHeaderValues,
|
parseHeaderValues,
|
||||||
readAsBlob,
|
readAsBlob,
|
||||||
sizeToHumanReadable,
|
sizeToHumanReadable,
|
||||||
|
splitCustomRegExpList,
|
||||||
} from "../../../lib/src/common/utils.ts";
|
} from "../../../lib/src/common/utils.ts";
|
||||||
import { arrayBufferToBase64Single, versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
import { arrayBufferToBase64Single, versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
||||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||||
@@ -1132,7 +1136,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
(paneEl) => {
|
(paneEl) => {
|
||||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleAppearance")).then((paneEl) => {
|
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleAppearance")).then((paneEl) => {
|
||||||
const languages = Object.fromEntries([
|
const languages = Object.fromEntries([
|
||||||
["", $msg("obsidianLiveSyncSettingTab.defaultLanguage")],
|
// ["", $msg("obsidianLiveSyncSettingTab.defaultLanguage")],
|
||||||
...SUPPORTED_I18N_LANGS.map((e) => [e, $t(`lang-${e}`)]),
|
...SUPPORTED_I18N_LANGS.map((e) => [e, $t(`lang-${e}`)]),
|
||||||
]) as Record<I18N_LANGS, string>;
|
]) as Record<I18N_LANGS, string>;
|
||||||
new Setting(paneEl).autoWireDropDown("displayLanguage", {
|
new Setting(paneEl).autoWireDropDown("displayLanguage", {
|
||||||
@@ -1144,6 +1148,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
onUpdate: visibleOnly(() => this.isConfiguredAs("showStatusOnEditor", true)),
|
onUpdate: visibleOnly(() => this.isConfiguredAs("showStatusOnEditor", true)),
|
||||||
});
|
});
|
||||||
new Setting(paneEl).autoWireToggle("showStatusOnStatusbar");
|
new Setting(paneEl).autoWireToggle("showStatusOnStatusbar");
|
||||||
|
new Setting(paneEl).autoWireToggle("hideFileWarningNotice");
|
||||||
});
|
});
|
||||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleLogging")).then((paneEl) => {
|
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleLogging")).then((paneEl) => {
|
||||||
paneEl.addClass("wizardHidden");
|
paneEl.addClass("wizardHidden");
|
||||||
@@ -2218,13 +2223,10 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
mount(MultipleRegExpControl, {
|
mount(MultipleRegExpControl, {
|
||||||
target: syncFilesSetting.controlEl,
|
target: syncFilesSetting.controlEl,
|
||||||
props: {
|
props: {
|
||||||
patterns: this.editingSettings.syncOnlyRegEx.split("|[]|"),
|
patterns: splitCustomRegExpList(this.editingSettings.syncOnlyRegEx, "|[]|"),
|
||||||
originals: [...this.editingSettings.syncOnlyRegEx.split("|[]|")],
|
originals: splitCustomRegExpList(this.editingSettings.syncOnlyRegEx, "|[]|"),
|
||||||
apply: async (newPatterns: string[]) => {
|
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||||
this.editingSettings.syncOnlyRegEx = newPatterns
|
this.editingSettings.syncOnlyRegEx = constructCustomRegExpList(newPatterns, "|[]|");
|
||||||
.map((e: string) => e.trim())
|
|
||||||
.filter((e) => e != "")
|
|
||||||
.join("|[]|");
|
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
this.display();
|
this.display();
|
||||||
},
|
},
|
||||||
@@ -2241,13 +2243,10 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
mount(MultipleRegExpControl, {
|
mount(MultipleRegExpControl, {
|
||||||
target: nonSyncFilesSetting.controlEl,
|
target: nonSyncFilesSetting.controlEl,
|
||||||
props: {
|
props: {
|
||||||
patterns: this.editingSettings.syncIgnoreRegEx.split("|[]|"),
|
patterns: splitCustomRegExpList(this.editingSettings.syncIgnoreRegEx, "|[]|"),
|
||||||
originals: [...this.editingSettings.syncIgnoreRegEx.split("|[]|")],
|
originals: splitCustomRegExpList(this.editingSettings.syncIgnoreRegEx, "|[]|"),
|
||||||
apply: async (newPatterns: string[]) => {
|
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||||
this.editingSettings.syncIgnoreRegEx = newPatterns
|
this.editingSettings.syncIgnoreRegEx = constructCustomRegExpList(newPatterns, "|[]|");
|
||||||
.map((e) => e.trim())
|
|
||||||
.filter((e) => e != "")
|
|
||||||
.join("|[]|");
|
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
this.display();
|
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) => {
|
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 defaultSkipPattern = "\\/node_modules\\/, \\/\\.git\\/, ^\\.git\\/, \\/obsidian-livesync\\/";
|
||||||
const defaultSkipPatternXPlat =
|
const defaultSkipPatternXPlat =
|
||||||
defaultSkipPattern + ",\\/workspace$ ,\\/workspace.json$,\\/workspace-mobile.json$";
|
defaultSkipPattern + ",\\/workspace$ ,\\/workspace.json$,\\/workspace-mobile.json$";
|
||||||
|
|
||||||
const pat = this.editingSettings.syncInternalFilesIgnorePatterns
|
const pat = splitCustomRegExpList(this.editingSettings.syncInternalFilesIgnorePatterns, ",");
|
||||||
.split(",")
|
|
||||||
.map((x) => x.trim())
|
|
||||||
.filter((x) => x != "");
|
|
||||||
const patSetting = new Setting(paneEl).setName("Ignore patterns").setClass("wizardHidden").setDesc("");
|
const patSetting = new Setting(paneEl).setName("Ignore patterns").setClass("wizardHidden").setDesc("");
|
||||||
|
|
||||||
mount(MultipleRegExpControl, {
|
mount(MultipleRegExpControl, {
|
||||||
@@ -2276,11 +2293,11 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
props: {
|
props: {
|
||||||
patterns: pat,
|
patterns: pat,
|
||||||
originals: [...pat],
|
originals: [...pat],
|
||||||
apply: async (newPatterns: string[]) => {
|
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||||
this.editingSettings.syncInternalFilesIgnorePatterns = newPatterns
|
this.editingSettings.syncInternalFilesIgnorePatterns = constructCustomRegExpList(
|
||||||
.map((e) => e.trim())
|
newPatterns,
|
||||||
.filter((e) => e != "")
|
","
|
||||||
.join(", ");
|
);
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
this.display();
|
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 addDefaultPatterns = async (patterns: string) => {
|
||||||
const oldList = this.editingSettings.syncInternalFilesIgnorePatterns
|
const oldList = splitCustomRegExpList(this.editingSettings.syncInternalFilesIgnorePatterns, ",");
|
||||||
.split(",")
|
const newList = splitCustomRegExpList(
|
||||||
.map((x) => x.trim())
|
patterns as unknown as typeof this.editingSettings.syncInternalFilesIgnorePatterns,
|
||||||
.filter((x) => x != "");
|
","
|
||||||
const newList = patterns
|
);
|
||||||
.split(",")
|
const allSet = new Set<CustomRegExpSource>([...oldList, ...newList]);
|
||||||
.map((x) => x.trim())
|
this.editingSettings.syncInternalFilesIgnorePatterns = constructCustomRegExpList([...allSet], ",");
|
||||||
.filter((x) => x != "");
|
|
||||||
const allSet = new Set([...oldList, ...newList]);
|
|
||||||
this.editingSettings.syncInternalFilesIgnorePatterns = [...allSet].join(", ");
|
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
this.display();
|
this.display();
|
||||||
};
|
};
|
||||||
@@ -2691,17 +2705,20 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
.setCta()
|
.setCta()
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
||||||
const ignorePatterns = this.plugin.settings.syncInternalFilesIgnorePatterns
|
const ignorePatterns = getFileRegExp(
|
||||||
.replace(/\n| /g, "")
|
this.plugin.settings,
|
||||||
.split(",")
|
"syncInternalFilesIgnorePatterns"
|
||||||
.filter((e) => e)
|
);
|
||||||
.map((e) => new RegExp(e, "i"));
|
const targetPatterns = getFileRegExp(
|
||||||
|
this.plugin.settings,
|
||||||
|
"syncInternalFilesTargetPatterns"
|
||||||
|
);
|
||||||
this.plugin.localDatabase.hashCaches.clear();
|
this.plugin.localDatabase.hashCaches.clear();
|
||||||
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
||||||
const files = this.plugin.settings.syncInternalFiles
|
const files = this.plugin.settings.syncInternalFiles
|
||||||
? await this.plugin.storageAccess.getFilesIncludeHidden(
|
? await this.plugin.storageAccess.getFilesIncludeHidden(
|
||||||
"/",
|
"/",
|
||||||
undefined,
|
targetPatterns,
|
||||||
ignorePatterns
|
ignorePatterns
|
||||||
)
|
)
|
||||||
: await this.plugin.storageAccess.getFileNames();
|
: await this.plugin.storageAccess.getFileNames();
|
||||||
@@ -2721,7 +2738,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
i++;
|
i++;
|
||||||
if (i % 25 == 0)
|
if (i % 25 == 0)
|
||||||
Logger(
|
Logger(
|
||||||
`Checking ${i}/${files.length} files \n`,
|
`Checking ${i}/${allPaths.length} files \n`,
|
||||||
LOG_LEVEL_NOTICE,
|
LOG_LEVEL_NOTICE,
|
||||||
"verify-processed"
|
"verify-processed"
|
||||||
);
|
);
|
||||||
@@ -3087,7 +3104,9 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
onUpdate: visibleOnly(() => this.isConfiguredAs("disableWorkerForGeneratingChunks", false)),
|
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) => {
|
void addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => {
|
||||||
new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch");
|
new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -377,6 +377,14 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
|||||||
name: "Minimum interval for syncing",
|
name: "Minimum interval for syncing",
|
||||||
desc: "The minimum interval for automatic synchronisation on event.",
|
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) {
|
function translateInfo(infoSrc: ConfigurationItem | undefined | false) {
|
||||||
if (!infoSrc) return false;
|
if (!infoSrc) return false;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
UXFolderInfo,
|
UXFolderInfo,
|
||||||
UXStat,
|
UXStat,
|
||||||
} from "../../lib/src/common/types";
|
} from "../../lib/src/common/types";
|
||||||
|
import type { CustomRegExp } from "../../lib/src/common/utils";
|
||||||
|
|
||||||
export interface StorageAccess {
|
export interface StorageAccess {
|
||||||
deleteVaultItem(file: FilePathWithPrefix | UXFileInfoStub | UXFolderInfo): Promise<void>;
|
deleteVaultItem(file: FilePathWithPrefix | UXFileInfoStub | UXFolderInfo): Promise<void>;
|
||||||
@@ -48,8 +49,8 @@ export interface StorageAccess {
|
|||||||
|
|
||||||
getFilesIncludeHidden(
|
getFilesIncludeHidden(
|
||||||
basePath: string,
|
basePath: string,
|
||||||
includeFilter?: RegExp[],
|
includeFilter?: CustomRegExp[],
|
||||||
excludeFilter?: RegExp[],
|
excludeFilter?: CustomRegExp[],
|
||||||
skipFolder?: string[]
|
skipFolder?: string[]
|
||||||
): Promise<FilePath[]>;
|
): Promise<FilePath[]>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user