mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-23 04:28:48 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cc70b985a | ||
|
|
0e81ec2586 | ||
|
|
bab66a64d7 | ||
|
|
477913456f | ||
|
|
b0661cdbab | ||
|
|
18f9a842b7 | ||
|
|
5130bc5f2a | ||
|
|
ca8af80a27 | ||
|
|
df273d273b | ||
|
|
23aa0a82ca | ||
|
|
8f488b205b | ||
|
|
893eac5c92 | ||
|
|
cd6946bce2 | ||
|
|
174ca08954 | ||
|
|
4af4d9c4bd | ||
|
|
1b7a25598a | ||
|
|
e2a01c14cc | ||
|
|
a623b987c8 | ||
|
|
db28b9ec11 | ||
|
|
b2fbbb38f5 |
128
.github/workflows/release.yml
vendored
128
.github/workflows/release.yml
vendored
@@ -10,19 +10,19 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
|
||||
submodules: recursive
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x' # You might need to adjust this value to your own version
|
||||
node-version: '24.x' # You might need to adjust this value to your own version
|
||||
# Get the version number and put it in a variable
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
echo "::set-output name=tag::$(git describe --abbrev=0 --tags)"
|
||||
echo "tag=$(git describe --abbrev=0 --tags)" >> $GITHUB_OUTPUT
|
||||
# Build the plugin
|
||||
- name: Build
|
||||
id: build
|
||||
@@ -36,59 +36,69 @@ jobs:
|
||||
cp main.js manifest.json styles.css README.md ${{ github.event.repository.name }}
|
||||
zip -r ${{ github.event.repository.name }}.zip ${{ github.event.repository.name }}
|
||||
# Create the release on github
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ github.ref }}
|
||||
# - name: Create Release
|
||||
# id: create_release
|
||||
# uses: actions/create-release@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# VERSION: ${{ steps.version.outputs.tag }}
|
||||
# with:
|
||||
# tag_name: ${{ steps.version.outputs.tag }}
|
||||
# release_name: ${{ steps.version.outputs.tag }}
|
||||
# draft: true
|
||||
# prerelease: false
|
||||
# # Upload the packaged release file
|
||||
# - name: Upload zip file
|
||||
# id: upload-zip
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./${{ github.event.repository.name }}.zip
|
||||
# asset_name: ${{ github.event.repository.name }}-${{ steps.version.outputs.tag }}.zip
|
||||
# asset_content_type: application/zip
|
||||
# # Upload the main.js
|
||||
# - name: Upload main.js
|
||||
# id: upload-main
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./main.js
|
||||
# asset_name: main.js
|
||||
# asset_content_type: text/javascript
|
||||
# # Upload the manifest.json
|
||||
# - name: Upload manifest.json
|
||||
# id: upload-manifest
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./manifest.json
|
||||
# asset_name: manifest.json
|
||||
# asset_content_type: application/json
|
||||
# # Upload the style.css
|
||||
# - name: Upload styles.css
|
||||
# id: upload-css
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./styles.css
|
||||
# asset_name: styles.css
|
||||
# asset_content_type: text/css
|
||||
- name: Create Release and Upload Assets
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
# Upload the packaged release file
|
||||
- name: Upload zip file
|
||||
id: upload-zip
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./${{ github.event.repository.name }}.zip
|
||||
asset_name: ${{ github.event.repository.name }}-${{ steps.version.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
# Upload the main.js
|
||||
- name: Upload main.js
|
||||
id: upload-main
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./main.js
|
||||
asset_name: main.js
|
||||
asset_content_type: text/javascript
|
||||
# Upload the manifest.json
|
||||
- name: Upload manifest.json
|
||||
id: upload-manifest
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./manifest.json
|
||||
asset_name: manifest.json
|
||||
asset_content_type: application/json
|
||||
# Upload the style.css
|
||||
- name: Upload styles.css
|
||||
id: upload-css
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./styles.css
|
||||
asset_name: styles.css
|
||||
asset_content_type: text/css
|
||||
# TODO: release notes???
|
||||
files: |
|
||||
${{ github.event.repository.name }}.zip
|
||||
main.js
|
||||
manifest.json
|
||||
styles.css
|
||||
name: ${{ steps.version.outputs.tag }}
|
||||
tag_name: ${{ steps.version.outputs.tag }}
|
||||
draft: true
|
||||
@@ -28,7 +28,7 @@ openssl ecparam -name secp521r1 -genkey -noout | openssl pkcs8 -topk8 -inform PE
|
||||
openssl ec -in private_key.pem -pubout -outform PEM -out public_key.pem
|
||||
```
|
||||
|
||||
> [!More tip]
|
||||
> [!TIP]
|
||||
> A key generator will be provided again in a future version of the user interface.
|
||||
|
||||
### 2. Configure CouchDB to accept JWT tokens
|
||||
|
||||
@@ -12,7 +12,7 @@ import inlineWorkerPlugin from "esbuild-plugin-inline-worker";
|
||||
import { terserOption } from "./terser.config.mjs";
|
||||
import path from "node:path";
|
||||
|
||||
const prod = process.argv[2] === "production";
|
||||
const prod = process.argv[2] === "production" || process.env?.BUILD_MODE === "production";
|
||||
const keepTest = true; //!prod;
|
||||
|
||||
const manifestJson = JSON.parse(fs.readFileSync("./manifest.json") + "");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.25.25",
|
||||
"version": "0.25.30",
|
||||
"minAppVersion": "0.9.12",
|
||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"author": "vorotamoroz",
|
||||
|
||||
42
package-lock.json
generated
42
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.25",
|
||||
"version": "0.25.30",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.25",
|
||||
"version": "0.25.30",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.808.0",
|
||||
@@ -19,7 +19,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.42",
|
||||
"octagonal-wheels": "^0.1.44",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "^0.22.0",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
@@ -8427,9 +8427,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9159,9 +9159,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/octagonal-wheels": {
|
||||
"version": "0.1.42",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.42.tgz",
|
||||
"integrity": "sha512-Hc2GWCtmG4+OzY9flY5vHjozUPuwsQoY7osG+I2QzACs8iTWrlAcw1re8FgU4vDC/to9rFogWfYWI8bNbr5j2w==",
|
||||
"version": "0.1.44",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.44.tgz",
|
||||
"integrity": "sha512-sUn/dkYQ2AbMB0R8CubVd75BjkcsteW9B14ArO99F6wM5JRwOo/yPIBBoxCUFE7JjBFOfuWG21C9E3NTga6XrA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"idb": "^8.0.3"
|
||||
@@ -10019,9 +10019,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode-generator": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz",
|
||||
"integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==",
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.2.tgz",
|
||||
"integrity": "sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
@@ -17832,9 +17832,9 @@
|
||||
"integrity": "sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -18353,9 +18353,9 @@
|
||||
}
|
||||
},
|
||||
"octagonal-wheels": {
|
||||
"version": "0.1.42",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.42.tgz",
|
||||
"integrity": "sha512-Hc2GWCtmG4+OzY9flY5vHjozUPuwsQoY7osG+I2QzACs8iTWrlAcw1re8FgU4vDC/to9rFogWfYWI8bNbr5j2w==",
|
||||
"version": "0.1.44",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.44.tgz",
|
||||
"integrity": "sha512-sUn/dkYQ2AbMB0R8CubVd75BjkcsteW9B14ArO99F6wM5JRwOo/yPIBBoxCUFE7JjBFOfuWG21C9E3NTga6XrA==",
|
||||
"requires": {
|
||||
"idb": "^8.0.3"
|
||||
}
|
||||
@@ -18967,9 +18967,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"qrcode-generator": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz",
|
||||
"integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw=="
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.2.tgz",
|
||||
"integrity": "sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw=="
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.25",
|
||||
"version": "0.25.30",
|
||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"main": "main.js",
|
||||
"type": "module",
|
||||
@@ -94,7 +94,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.42",
|
||||
"octagonal-wheels": "^0.1.44",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "^0.22.0",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
|
||||
@@ -65,5 +65,5 @@ export const ICHeaderLength = ICHeader.length;
|
||||
export const ICXHeader = "ix:";
|
||||
|
||||
export const FileWatchEventQueueMax = 10;
|
||||
export const configURIBase = "obsidian://setuplivesync?settings=";
|
||||
export const configURIBaseQR = "obsidian://setuplivesync?settingsQR=";
|
||||
|
||||
export { configURIBase, configURIBaseQR } from "../lib/src/common/types.ts";
|
||||
|
||||
@@ -566,119 +566,3 @@ export function updatePreviousExecutionTime(key: string, timeDelta: number = 0)
|
||||
}
|
||||
waitingTasks[key].leastNext = Math.max(Date.now() + timeDelta, waitingTasks[key].leastNext);
|
||||
}
|
||||
|
||||
const prefixMapObject = {
|
||||
s: {
|
||||
1: "V",
|
||||
2: "W",
|
||||
3: "X",
|
||||
4: "Y",
|
||||
5: "Z",
|
||||
},
|
||||
o: {
|
||||
1: "v",
|
||||
2: "w",
|
||||
3: "x",
|
||||
4: "y",
|
||||
5: "z",
|
||||
},
|
||||
} as Record<string, Record<number, string>>;
|
||||
|
||||
const decodePrefixMapObject = Object.fromEntries(
|
||||
Object.entries(prefixMapObject).flatMap(([prefix, map]) =>
|
||||
Object.entries(map).map(([len, char]) => [char, { prefix, len: parseInt(len) }])
|
||||
)
|
||||
);
|
||||
|
||||
const prefixMapNumber = {
|
||||
n: {
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
4: "d",
|
||||
5: "e",
|
||||
},
|
||||
N: {
|
||||
1: "A",
|
||||
2: "B",
|
||||
3: "C",
|
||||
4: "D",
|
||||
5: "E",
|
||||
},
|
||||
} as Record<string, Record<number, string>>;
|
||||
|
||||
const decodePrefixMapNumber = Object.fromEntries(
|
||||
Object.entries(prefixMapNumber).flatMap(([prefix, map]) =>
|
||||
Object.entries(map).map(([len, char]) => [char, { prefix, len: parseInt(len) }])
|
||||
)
|
||||
);
|
||||
export function encodeAnyArray(obj: any[]): string {
|
||||
const tempArray = obj.map((v) => {
|
||||
if (v === null) return "n";
|
||||
if (v === false) return "f";
|
||||
if (v === true) return "t";
|
||||
if (v === undefined) return "u";
|
||||
if (typeof v == "number") {
|
||||
const b36 = v.toString(36);
|
||||
const strNum = v.toString();
|
||||
const expression = b36.length < strNum.length ? "N" : "n";
|
||||
const encodedStr = expression == "N" ? b36 : strNum;
|
||||
const len = encodedStr.length.toString(36);
|
||||
const lenLen = len.length;
|
||||
|
||||
const prefix2 = prefixMapNumber[expression][lenLen];
|
||||
return prefix2 + len + encodedStr;
|
||||
}
|
||||
const str = typeof v == "string" ? v : JSON.stringify(v);
|
||||
const prefix = typeof v == "string" ? "s" : "o";
|
||||
const length = str.length.toString(36);
|
||||
const lenLen = length.length;
|
||||
|
||||
const prefix2 = prefixMapObject[prefix][lenLen];
|
||||
return prefix2 + length + str;
|
||||
});
|
||||
const w = tempArray.join("");
|
||||
return w;
|
||||
}
|
||||
|
||||
const decodeMapConstant = {
|
||||
u: undefined,
|
||||
n: null,
|
||||
f: false,
|
||||
t: true,
|
||||
} as Record<string, any>;
|
||||
export function decodeAnyArray(str: string): any[] {
|
||||
const result = [];
|
||||
let i = 0;
|
||||
while (i < str.length) {
|
||||
const char = str[i];
|
||||
i++;
|
||||
if (char in decodeMapConstant) {
|
||||
result.push(decodeMapConstant[char]);
|
||||
continue;
|
||||
}
|
||||
if (char in decodePrefixMapNumber) {
|
||||
const { prefix, len } = decodePrefixMapNumber[char];
|
||||
const lenStr = str.substring(i, i + len);
|
||||
i += len;
|
||||
const radix = prefix == "N" ? 36 : 10;
|
||||
const lenNum = parseInt(lenStr, 36);
|
||||
const value = str.substring(i, i + lenNum);
|
||||
i += lenNum;
|
||||
result.push(parseInt(value, radix));
|
||||
continue;
|
||||
}
|
||||
const { prefix, len } = decodePrefixMapObject[char];
|
||||
const lenStr = str.substring(i, i + len);
|
||||
i += len;
|
||||
const lenNum = parseInt(lenStr, 36);
|
||||
const value = str.substring(i, i + lenNum);
|
||||
i += lenNum;
|
||||
if (prefix == "s") {
|
||||
result.push(value);
|
||||
} else {
|
||||
result.push(JSON.parse(value));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -722,6 +722,13 @@ Offline Changed files: ${processFiles.length}`;
|
||||
} else {
|
||||
this._log(`Object merge is not applicable.`, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
// const pat = this.settings.syncInternalFileOverwritePatterns;
|
||||
const regExp = getFileRegExp(this.settings, "syncInternalFileOverwritePatterns");
|
||||
if (regExp.some((r) => r.test(stripAllPrefixes(path)))) {
|
||||
this._log(`Overwrite rule applied for conflicted hidden file: ${path}`, LOG_LEVEL_INFO);
|
||||
await this.resolveByNewerEntry(id, path, doc, revA, revB);
|
||||
return [];
|
||||
}
|
||||
return [{ path, revA, revB, id, doc }];
|
||||
}
|
||||
// When not JSON file, resolve conflicts by choosing a newer one.
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: 6617500bc5...86b0a95d56
@@ -1,6 +1,7 @@
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
import { ExtraSuffixIndexedDB } from "../../lib/src/common/types";
|
||||
|
||||
export class ModulePouchDB extends AbstractModule {
|
||||
_createPouchDBInstance<T extends object>(
|
||||
@@ -12,7 +13,7 @@ export class ModulePouchDB extends AbstractModule {
|
||||
optionPass.adapter = "indexeddb";
|
||||
//@ts-ignore :missing def
|
||||
optionPass.purged_infos_limit = 1;
|
||||
return new PouchDB(name + "-indexeddb", optionPass);
|
||||
return new PouchDB(name + ExtraSuffixIndexedDB, optionPass);
|
||||
}
|
||||
return new PouchDB(name, optionPass);
|
||||
}
|
||||
|
||||
@@ -34,12 +34,17 @@ import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import { P2PLogCollector } from "../../lib/src/replication/trystero/P2PReplicatorCore.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { LiveSyncError } from "@/lib/src/common/LSError.ts";
|
||||
|
||||
// This module cannot be a core module because it depends on the Obsidian UI.
|
||||
|
||||
// DI the log again.
|
||||
setGlobalLogFunction((message: any, level?: number, key?: string) => {
|
||||
const entry = { message, level, key } as LogEntry;
|
||||
const messageX =
|
||||
message instanceof Error
|
||||
? new LiveSyncError("[Error Logged]: " + message.message, { cause: message })
|
||||
: message;
|
||||
const entry = { message: messageX, level, key } as LogEntry;
|
||||
logStore.enqueue(entry);
|
||||
});
|
||||
let recentLogs = [] as string[];
|
||||
@@ -398,19 +403,29 @@ export class ModuleLog extends AbstractObsidianModule {
|
||||
const vaultName = this.services.vault.getVaultName();
|
||||
const now = new Date();
|
||||
const timestamp = now.toLocaleString();
|
||||
let errorInfo = "";
|
||||
if (message instanceof Error) {
|
||||
if (message instanceof LiveSyncError) {
|
||||
errorInfo = `${message.cause?.name}:${message.cause?.message}\n[StackTrace]: ${message.stack}\n[CausedBy]: ${message.cause?.stack}`;
|
||||
} else {
|
||||
const thisStack = new Error().stack;
|
||||
errorInfo = `${message.name}:${message.message}\n[StackTrace]: ${message.stack}\n[LogCallStack]: ${thisStack}`;
|
||||
}
|
||||
}
|
||||
const messageContent =
|
||||
typeof message == "string"
|
||||
? message
|
||||
: message instanceof Error
|
||||
? `${message.name}:${message.message}`
|
||||
? `${errorInfo}`
|
||||
: JSON.stringify(message, null, 2);
|
||||
if (message instanceof Error) {
|
||||
// debugger;
|
||||
console.dir(message.stack);
|
||||
}
|
||||
const newMessage = timestamp + "->" + messageContent;
|
||||
|
||||
console.log(vaultName + ":" + newMessage);
|
||||
if (message instanceof Error) {
|
||||
console.error(vaultName + ":" + newMessage);
|
||||
} else if (level >= LOG_LEVEL_INFO) {
|
||||
console.log(vaultName + ":" + newMessage);
|
||||
} else {
|
||||
console.debug(vaultName + ":" + newMessage);
|
||||
}
|
||||
if (!this.settings?.showOnlyIconsOnEditor) {
|
||||
this.statusLog.value = messageContent;
|
||||
}
|
||||
|
||||
@@ -316,6 +316,11 @@ export class ModuleObsidianSettings extends AbstractObsidianModule {
|
||||
// this.core.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim());
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
}
|
||||
|
||||
private _currentSettings(): ObsidianLiveSyncSettings {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
super.onBindFunction(core, services);
|
||||
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
|
||||
@@ -323,6 +328,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule {
|
||||
services.setting.handleDecryptSettings(this._decryptSettings.bind(this));
|
||||
services.setting.handleAdjustSettings(this._adjustSettings.bind(this));
|
||||
services.setting.handleLoadSettings(this._loadSettings.bind(this));
|
||||
services.setting.handleCurrentSettings(this._currentSettings.bind(this));
|
||||
services.setting.handleSaveDeviceAndVaultName(this._saveDeviceAndVaultName.bind(this));
|
||||
services.setting.handleSaveSettingData(this._saveSettingData.bind(this));
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ export class ModuleSetupObsidian extends AbstractObsidianModule {
|
||||
async encodeQR() {
|
||||
const settingString = encodeSettingsToQRCodeData(this.settings);
|
||||
const codeSVG = encodeQR(settingString, OutputFormat.SVG);
|
||||
if (codeSVG == "") {
|
||||
return "";
|
||||
}
|
||||
const msg = $msg("Setup.QRCode", { qr_image: codeSVG });
|
||||
await this.core.confirm.confirmWithMessage("Settings QR Code", msg, ["OK"], "OK");
|
||||
return await Promise.resolve(codeSVG);
|
||||
|
||||
@@ -3,12 +3,16 @@ import {
|
||||
E2EEAlgorithms,
|
||||
type HashAlgorithm,
|
||||
LOG_LEVEL_NOTICE,
|
||||
SuffixDatabaseName,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
|
||||
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
|
||||
import type { PageFunctions } from "./SettingPane.ts";
|
||||
import { visibleOnly } from "./SettingPane.ts";
|
||||
import { PouchDB } from "../../../lib/src/pouchdb/pouchdb-browser";
|
||||
import { ExtraSuffixIndexedDB } from "../../../lib/src/common/types.ts";
|
||||
import { migrateDatabases } from "./settingUtils.ts";
|
||||
|
||||
export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void {
|
||||
void addPanel(paneEl, "Compatibility (Metadata)").then((paneEl) => {
|
||||
@@ -26,17 +30,88 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Compatibility (Database structure)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("useIndexedDBAdapter", { invert: true, holdValue: true });
|
||||
|
||||
// new Setting(paneEl)
|
||||
// .autoWireToggle("doNotUseFixedRevisionForChunks", { holdValue: true })
|
||||
// .setClass("wizardHidden");
|
||||
const migrateAllToIndexedDB = async () => {
|
||||
const dbToName = this.plugin.localDatabase.dbname + SuffixDatabaseName + ExtraSuffixIndexedDB;
|
||||
const options = {
|
||||
adapter: "indexeddb",
|
||||
//@ts-ignore :missing def
|
||||
purged_infos_limit: 1,
|
||||
auto_compaction: false,
|
||||
deterministic_revs: true,
|
||||
};
|
||||
const openTo = () => {
|
||||
return new PouchDB(dbToName, options);
|
||||
};
|
||||
if (await migrateDatabases("to IndexedDB", this.plugin.localDatabase.localDatabase, openTo)) {
|
||||
Logger(
|
||||
"Migration to IndexedDB completed. Obsidian will be restarted with new configuration immediately.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.plugin.settings.useIndexedDBAdapter = true;
|
||||
await this.services.setting.saveSettingData();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
};
|
||||
const migrateAllToIDB = async () => {
|
||||
const dbToName = this.plugin.localDatabase.dbname + SuffixDatabaseName;
|
||||
const options = {
|
||||
adapter: "idb",
|
||||
auto_compaction: false,
|
||||
deterministic_revs: true,
|
||||
};
|
||||
const openTo = () => {
|
||||
return new PouchDB(dbToName, options);
|
||||
};
|
||||
if (await migrateDatabases("to IDB", this.plugin.localDatabase.localDatabase, openTo)) {
|
||||
Logger(
|
||||
"Migration to IDB completed. Obsidian will be restarted with new configuration immediately.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.plugin.settings.useIndexedDBAdapter = false;
|
||||
await this.services.setting.saveSettingData();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
};
|
||||
{
|
||||
const infoClass = this.editingSettings.useIndexedDBAdapter ? "op-warn" : "op-warn-info";
|
||||
paneEl.createDiv({
|
||||
text: "The IndexedDB adapter often offers superior performance in certain scenarios, but it has been found to cause memory leaks when used with LiveSync mode. When using LiveSync mode, please use IDB adapter instead.",
|
||||
cls: infoClass,
|
||||
});
|
||||
paneEl.createDiv({
|
||||
text: "Changing this setting requires migrating existing data (a bit time may be taken) and restarting Obsidian. Please make sure to back up your data before proceeding.",
|
||||
cls: "op-warn-info",
|
||||
});
|
||||
const setting = new Setting(paneEl)
|
||||
.setName("Database Adapter")
|
||||
.setDesc("Select the database adapter to use. ");
|
||||
const el = setting.controlEl.createDiv({});
|
||||
el.setText(`Current adapter: ${this.editingSettings.useIndexedDBAdapter ? "IndexedDB" : "IDB"}`);
|
||||
if (!this.editingSettings.useIndexedDBAdapter) {
|
||||
setting.addButton((button) => {
|
||||
button.setButtonText("Switch to IndexedDB").onClick(async () => {
|
||||
Logger("Migrating all data to IndexedDB...", LOG_LEVEL_NOTICE);
|
||||
await migrateAllToIndexedDB();
|
||||
Logger(
|
||||
"Migration to IndexedDB completed. Please switch the adapter and restart Obsidian.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setting.addButton((button) => {
|
||||
button.setButtonText("Switch to IDB").onClick(async () => {
|
||||
Logger("Migrating all data to IDB...", LOG_LEVEL_NOTICE);
|
||||
await migrateAllToIDB();
|
||||
Logger(
|
||||
"Migration to IDB completed. Please switch the adapter and restart Obsidian.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
new Setting(paneEl).autoWireToggle("handleFilenameCaseSensitive", { holdValue: true }).setClass("wizardHidden");
|
||||
|
||||
this.addOnSaved("useIndexedDBAdapter", async () => {
|
||||
await this.saveAllDirtySettings();
|
||||
await this.rebuildDB("localOnly");
|
||||
});
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => {
|
||||
|
||||
@@ -117,5 +117,26 @@ export function paneSelector(this: ObsidianLiveSyncSettingTab, paneEl: HTMLEleme
|
||||
await addDefaultPatterns(defaultSkipPatternXPlat);
|
||||
});
|
||||
});
|
||||
|
||||
const overwritePatterns = new Setting(paneEl)
|
||||
.setName("Overwrite patterns")
|
||||
.setClass("wizardHidden")
|
||||
.setDesc("Patterns to match files for overwriting instead of merging");
|
||||
const patTarget2 = splitCustomRegExpList(this.editingSettings.syncInternalFileOverwritePatterns, ",");
|
||||
mount(MultipleRegExpControl, {
|
||||
target: overwritePatterns.controlEl,
|
||||
props: {
|
||||
patterns: patTarget2,
|
||||
originals: [...patTarget2],
|
||||
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||
this.editingSettings.syncInternalFileOverwritePatterns = constructCustomRegExpList(
|
||||
newPatterns,
|
||||
","
|
||||
);
|
||||
await this.saveAllDirtySettings();
|
||||
this.display();
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { escapeStringToHTML } from "octagonal-wheels/string";
|
||||
import { E2EEAlgorithmNames, type ObsidianLiveSyncSettings } from "../../../lib/src/common/types";
|
||||
import {
|
||||
E2EEAlgorithmNames,
|
||||
MILESTONE_DOCID,
|
||||
NODEINFO_DOCID,
|
||||
type ObsidianLiveSyncSettings,
|
||||
} from "../../../lib/src/common/types";
|
||||
import {
|
||||
pickCouchDBSyncSettings,
|
||||
pickBucketSyncSettings,
|
||||
@@ -7,6 +12,7 @@ import {
|
||||
pickEncryptionSettings,
|
||||
} from "../../../lib/src/common/utils";
|
||||
import { getConfig, type AllSettingItemKey } from "./settingConstants";
|
||||
import { LOG_LEVEL_NOTICE, Logger } from "octagonal-wheels/common/logger";
|
||||
|
||||
/**
|
||||
* Generates a summary of P2P configuration settings
|
||||
@@ -76,3 +82,73 @@ export function getSummaryFromPartialSettings(setting: Partial<ObsidianLiveSyncS
|
||||
}
|
||||
return outputSummary;
|
||||
}
|
||||
|
||||
// Migration or de-migration helper functions
|
||||
|
||||
/**
|
||||
* Copy document from one database to another for migration purposes
|
||||
* @param docName document ID
|
||||
* @param dbFrom source database
|
||||
* @param dbTo destination database
|
||||
* @returns
|
||||
*/
|
||||
export async function copyMigrationDocs(docName: string, dbFrom: PouchDB.Database, dbTo: PouchDB.Database) {
|
||||
try {
|
||||
const doc = await dbFrom.get(docName);
|
||||
delete (doc as any)._rev;
|
||||
await dbTo.put(doc);
|
||||
} catch (e) {
|
||||
if ((e as any).status === 404) {
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
type PouchDBOpenFunction = () => Promise<PouchDB.Database> | PouchDB.Database;
|
||||
|
||||
/**
|
||||
* Migrate databases from one to another
|
||||
* @param operationName Name of the migration operation
|
||||
* @param from source database
|
||||
* @param openTo function to open destination database
|
||||
* @returns True if migration succeeded
|
||||
*/
|
||||
export async function migrateDatabases(operationName: string, from: PouchDB.Database, openTo: PouchDBOpenFunction) {
|
||||
const dbTo = await openTo();
|
||||
await dbTo.info(); // ensure created
|
||||
Logger(`Opening destination database for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
// destroy existing data
|
||||
await dbTo.destroy();
|
||||
Logger(`Destroyed existing destination database for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
|
||||
const dbTo2 = await openTo();
|
||||
const info2 = await dbTo2.info(); // ensure created
|
||||
console.log(info2);
|
||||
Logger(`Re-created destination database for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
|
||||
const info = await from.info();
|
||||
const totalDocs = info.doc_count || 0;
|
||||
const result = await from.replicate
|
||||
.to(dbTo2, {
|
||||
//@ts-ignore Missing in typedefs
|
||||
style: "all_docs",
|
||||
})
|
||||
.on("change", (info) => {
|
||||
Logger(
|
||||
`Replicating... Docs replicated: ${info.docs_written} / ${totalDocs}`,
|
||||
LOG_LEVEL_NOTICE,
|
||||
"migration"
|
||||
);
|
||||
});
|
||||
if (result.ok) {
|
||||
Logger(`Replication completed for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
} else {
|
||||
throw new Error(`Replication failed for migration: ${operationName}.`);
|
||||
}
|
||||
await copyMigrationDocs(MILESTONE_DOCID, from, dbTo2);
|
||||
await copyMigrationDocs(NODEINFO_DOCID, from, dbTo2);
|
||||
Logger(`Copied migration documents for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
await dbTo2.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -243,6 +243,10 @@
|
||||
disabled={!isUseJWT}
|
||||
></textarea>
|
||||
</InputRow>
|
||||
<InfoNote>
|
||||
For HS256/HS512 algorithms, provide the shared secret key. For ES256/ES512 algorithms, provide the pkcs8
|
||||
PEM-formatted private key.
|
||||
</InfoNote>
|
||||
<InputRow label="JWT Key ID (kid)">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -62,12 +62,19 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
||||
|
||||
_wireUpEvents() {
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
this.localDatabase.settings = settings;
|
||||
setLang(settings.displayLanguage);
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
});
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
fireAndForget(() => this.core.services.setting.realiseSetting());
|
||||
fireAndForget(async () => {
|
||||
try {
|
||||
await this.core.services.setting.realiseSetting();
|
||||
const lang = this.core.services.setting.currentSettings()?.displayLanguage ?? undefined;
|
||||
if (lang !== undefined) {
|
||||
setLang(this.core.services.setting.currentSettings()?.displayLanguage);
|
||||
}
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
} catch (e) {
|
||||
this._log(`Error in Setting Save Event`, LOG_LEVEL_NOTICE);
|
||||
this._log(e, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
});
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
183
updates.md
183
updates.md
@@ -4,6 +4,72 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
|
||||
|
||||
## 0.25.30
|
||||
|
||||
17th November, 2025
|
||||
|
||||
So sorry for the quick follow-up release, due to a humble mistake in a quick causing a matter.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now we can save settings correctly again (#756).
|
||||
|
||||
|
||||
## ~~0.25.28~~ 0.25.29
|
||||
(0.25.28 was skipped due to a packaging issue.)
|
||||
|
||||
17th November, 2025
|
||||
|
||||
### New feature
|
||||
- We can now configure hidden file synchronisation to always overwrite with the latest version (#579).
|
||||
|
||||
### Fixed
|
||||
- Timing dependency issues during initialisation have been mitigated (#714)
|
||||
|
||||
### Improved
|
||||
- Error logs now contain stack-traces for better inspection.
|
||||
|
||||
## 0.25.27
|
||||
|
||||
12th November, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Now we can switch the database adapter between IndexedDB and IDB without rebuilding (#747).
|
||||
- Just a local migration will be required, but faster than a full rebuild.
|
||||
- No longer checking for the adapter by `Doctor`.
|
||||
|
||||
### Changes
|
||||
|
||||
- The default adapter is reverted to IDB to avoid memory leaks (#747).
|
||||
|
||||
### Fixed (?)
|
||||
|
||||
- Reverted QR code library to v1.4.4 (To make sure #752).
|
||||
|
||||
|
||||
## 0.25.26
|
||||
|
||||
07th November, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Some JWT notes have been added to the setting dialogue (#742).
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer wrong values encoded into the QR code.
|
||||
- We can acknowledge why the QR codes have not been generated.
|
||||
- Probably too large a dataset to encode. When this happens, please consider using Setup-URI via text instead of QR code, or reduce the settings temporarily.
|
||||
|
||||
### Refactored
|
||||
|
||||
- Some dependencies have been updated.
|
||||
- Internal functions have been modularised into `octagonal-wheels` packages and are well tested.
|
||||
- `dataobject/Computed` for caching computed values.
|
||||
- `encodeAnyArray/decodeAnyArray` for encoding and decoding any array-like data into compact strings (#729).
|
||||
- Fixed importing from the parent project in library codes. (#729).
|
||||
|
||||
## 0.25.25
|
||||
|
||||
06th November, 2025
|
||||
@@ -34,122 +100,5 @@ And, tips about JWT Authentication on CouchDB have been added to the documentati
|
||||
- The notification area is no longer imposing, distracting, and overwhelming.
|
||||
- With a pale background, but bordered and with icons.
|
||||
|
||||
## 0.25.24
|
||||
|
||||
04th November, 2025
|
||||
|
||||
(Beta release notes have been consolidated to this note).
|
||||
|
||||
### Guidance and UI improvements!
|
||||
|
||||
Since several issues were pointed out, our setup procedure had been quite `system-oriented`. This is not good for users. Therefore, I have changed the procedure to be more `goal-oriented`. I have made extensive use of Svelte, resulting in a very straightforward setup.
|
||||
While I would like to accelerate documentation and i18n adoption, I do not want to confuse everyone who's already working on it. Therefore, I have decided to release a Beta version at this stage. Significant changes are not expected from this point onward, so I will proceed to stabilise the codebase. (However, this is significant).
|
||||
|
||||
### TURN server support and important notice
|
||||
|
||||
TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P
|
||||
connections. In most cases, you do not need to set up a TURN server.
|
||||
|
||||
Using public TURN servers may have privacy implications, as your data will be relayed through third-party
|
||||
servers. Even if your data are encrypted, your existence may be known to them. Please ensure you trust the TURN
|
||||
server provider before using their services. Also your `network administrator` too. You should consider setting
|
||||
up your own TURN server for your FQDN, if possible.
|
||||
|
||||
### New features
|
||||
|
||||
- We can use the TURN server for P2P connections now.
|
||||
|
||||
### Fixed
|
||||
|
||||
- P2P Replication got more robust and stable.
|
||||
- Update [Trystero](https://github.com/dmotz/trystero) to the official v0.22.0!
|
||||
- Fixed a bug that caused P2P connections to drop or (unwanted reconnection to the relay server) unexpectedly in some environments.
|
||||
- Now, the connection status is more accurately reported.
|
||||
- While in the background, the connection to the signalling server is now disconnected to save resources.
|
||||
- When returning to the foreground, it will not reconnect automatically for safety. Please reconnect manually.
|
||||
- All connection configurations should be edited in each dedicated dialogue now.
|
||||
- No longer will larger files create chunks during preparing `Reset Synchronisation on This Device`.
|
||||
- Now hidden file synchronisation respects the filters correctly (#631, #735)
|
||||
- And `ignore-files` settings are also respected and surely read during the start-up.
|
||||
|
||||
### Behaviour changes
|
||||
|
||||
- The setup wizard is now more `goal-oriented`. Brand-new screens are introduced.
|
||||
- `Fetch everything` and `Rebuild everything` are now `Reset Synchronisation on This Device` and `Overwrite Server Data with This Device's Files`.
|
||||
- Remote configuration and E2EE settings are now separated into each modal dialogue.
|
||||
- Remote configuration is now more straightforward. And if we need the rebuild (No... `Overwrite Server Data with This Device's Files`), it is now clearly indicated.
|
||||
- Peer-to-Peer settings are also separated into their own modal dialogue (still in progress, and we need to open a P2P pane, still).
|
||||
- Setup-URI, and Report for the Issue are now not copied to the clipboard automatically. Instead, there are copy-dialogue and buttons to copy them explicitly.
|
||||
- This is to avoid confusion for users who do not want to use these features.
|
||||
- No longer optional features are introduced during the setup, or `Reset Synchronisation on This Device`, `Overwrite Server Data with This Device's Files`.
|
||||
- This is to avoid confusion for users who do not want to use these features. Instead, we will be informed that optional features are available after the setup is completed.
|
||||
- We cannot perform `Fetch everything` and `Rebuild everything` (Removed, so the old name) without restarting Obsidian now.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Setup QR Code generation is separated into a src/lib/src/API/processSetting.ts file. Please use it as a subrepository if you want to generate QR codes in your own application.
|
||||
- Setup-URI is also separated into a src/lib/src/API/processSetting.ts
|
||||
- Some direct access to web APIs is now wrapped into the services layer.
|
||||
|
||||
### Dependency updates
|
||||
|
||||
- Many dependencies are updated. Please see `package.json`.
|
||||
- This is the hardest part of this update. I read most of the changes in the dependencies. If you find any extra information, please let me know.
|
||||
- As upgrading TypeScript, Fixed many UInt8Array<ArrayBuffer> and Uint8Array type mismatches.
|
||||
-
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Sending configuration via Peer-to-Peer connection is not compatible with older versions.
|
||||
- Please upgrade all devices to v0.25.24.beta1 or later to use this feature again.
|
||||
- This is due to security improvements in the encryption scheme.
|
||||
|
||||
## 0.25.23
|
||||
|
||||
26th October, 2025
|
||||
|
||||
The next version we are preparing (you know that as 0.25.23.beta1) is now still on beta, resulting in this rather unfortunate versioning situation. Apologies for the confusion. The next v0.25.23.beta2 will be v0.25.24.beta1. In other words, this is a v0.25.22.patch-1 actually, but possibly not allowed by Obsidian's rule.
|
||||
(Perhaps we ought to declare 1.0.0 with a little more confidence. The current minor part has been effectively a major one for a long time. If it were 1.22.1 and 1.23.0.beta1, no confusion ).
|
||||
|
||||
### Fixed
|
||||
|
||||
- We are now able to enable optional features correctly again (#732).
|
||||
- No longer oversized files have been processed, furthermore.
|
||||
|
||||
- Before creating a chunk, the file is verified as the target.
|
||||
- The behaviour upon receiving replication has been changed as follows:
|
||||
- If the remote file is oversized, it is ignored.
|
||||
- If not, but while the local file is oversized, it is also ignored.
|
||||
|
||||
- We are now able to enable optional features correctly again (#732).
|
||||
- No longer oversized files have been processed, furthermore.
|
||||
- Before creating a chunk, the file is verified as the target.
|
||||
- The behaviour upon receiving replication has been changed as follows:
|
||||
- If the remote file is oversized, it is ignored.
|
||||
- If not, but while the local file is oversized, it is also ignored.
|
||||
|
||||
## 0.25.22
|
||||
|
||||
15th October, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug that caused wrong event bindings and flag inversion (#727)
|
||||
- This caused following issues:
|
||||
- In some cases, settings changes were not applied or saved correctly.
|
||||
- Automatic synchronisation did not begin correctly.
|
||||
|
||||
### Improved
|
||||
|
||||
- Too large diffs are not shown in the file comparison view, due to performance reasons.
|
||||
|
||||
### Notes
|
||||
|
||||
- The checking algorithm implemented in 0.25.20 is also raised as PR (#237). And completely I merged it manually.
|
||||
- Sorry for lacking merging this PR, and let me say thanks to the great contribution, @bioluks !
|
||||
- Known issues:
|
||||
- Sync on Editor save seems not to work correctly in some cases.
|
||||
- I am investigating this issue. If you have any information, please let me know.
|
||||
|
||||
Older notes are in
|
||||
[updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
||||
|
||||
116
updates_old.md
116
updates_old.md
@@ -9,6 +9,122 @@ I have now rewritten the E2EE code to be more robust and easier to understand. I
|
||||
As a result, this is the first time in a while that forward compatibility has been broken. We have also taken the opportunity to change all metadata to use encryption rather than obfuscation. Furthermore, the `Dynamic Iteration Count` setting is now redundant and has been moved to the `Patches` pane in the settings. Thanks to Rabin-Karp, the eden setting is also no longer necessary and has been relocated accordingly. Therefore, v0.25.0 represents a legitimate and correct evolution.
|
||||
|
||||
---
|
||||
## 0.25.24
|
||||
|
||||
04th November, 2025
|
||||
|
||||
(Beta release notes have been consolidated to this note).
|
||||
|
||||
### Guidance and UI improvements!
|
||||
|
||||
Since several issues were pointed out, our setup procedure had been quite `system-oriented`. This is not good for users. Therefore, I have changed the procedure to be more `goal-oriented`. I have made extensive use of Svelte, resulting in a very straightforward setup.
|
||||
While I would like to accelerate documentation and i18n adoption, I do not want to confuse everyone who's already working on it. Therefore, I have decided to release a Beta version at this stage. Significant changes are not expected from this point onward, so I will proceed to stabilise the codebase. (However, this is significant).
|
||||
|
||||
### TURN server support and important notice
|
||||
|
||||
TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P
|
||||
connections. In most cases, you do not need to set up a TURN server.
|
||||
|
||||
Using public TURN servers may have privacy implications, as your data will be relayed through third-party
|
||||
servers. Even if your data are encrypted, your existence may be known to them. Please ensure you trust the TURN
|
||||
server provider before using their services. Also your `network administrator` too. You should consider setting
|
||||
up your own TURN server for your FQDN, if possible.
|
||||
|
||||
### New features
|
||||
|
||||
- We can use the TURN server for P2P connections now.
|
||||
|
||||
### Fixed
|
||||
|
||||
- P2P Replication got more robust and stable.
|
||||
- Update [Trystero](https://github.com/dmotz/trystero) to the official v0.22.0!
|
||||
- Fixed a bug that caused P2P connections to drop or (unwanted reconnection to the relay server) unexpectedly in some environments.
|
||||
- Now, the connection status is more accurately reported.
|
||||
- While in the background, the connection to the signalling server is now disconnected to save resources.
|
||||
- When returning to the foreground, it will not reconnect automatically for safety. Please reconnect manually.
|
||||
- All connection configurations should be edited in each dedicated dialogue now.
|
||||
- No longer will larger files create chunks during preparing `Reset Synchronisation on This Device`.
|
||||
- Now hidden file synchronisation respects the filters correctly (#631, #735)
|
||||
- And `ignore-files` settings are also respected and surely read during the start-up.
|
||||
|
||||
### Behaviour changes
|
||||
|
||||
- The setup wizard is now more `goal-oriented`. Brand-new screens are introduced.
|
||||
- `Fetch everything` and `Rebuild everything` are now `Reset Synchronisation on This Device` and `Overwrite Server Data with This Device's Files`.
|
||||
- Remote configuration and E2EE settings are now separated into each modal dialogue.
|
||||
- Remote configuration is now more straightforward. And if we need the rebuild (No... `Overwrite Server Data with This Device's Files`), it is now clearly indicated.
|
||||
- Peer-to-Peer settings are also separated into their own modal dialogue (still in progress, and we need to open a P2P pane, still).
|
||||
- Setup-URI, and Report for the Issue are now not copied to the clipboard automatically. Instead, there are copy-dialogue and buttons to copy them explicitly.
|
||||
- This is to avoid confusion for users who do not want to use these features.
|
||||
- No longer optional features are introduced during the setup, or `Reset Synchronisation on This Device`, `Overwrite Server Data with This Device's Files`.
|
||||
- This is to avoid confusion for users who do not want to use these features. Instead, we will be informed that optional features are available after the setup is completed.
|
||||
- We cannot perform `Fetch everything` and `Rebuild everything` (Removed, so the old name) without restarting Obsidian now.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Setup QR Code generation is separated into a src/lib/src/API/processSetting.ts file. Please use it as a subrepository if you want to generate QR codes in your own application.
|
||||
- Setup-URI is also separated into a src/lib/src/API/processSetting.ts
|
||||
- Some direct access to web APIs is now wrapped into the services layer.
|
||||
|
||||
### Dependency updates
|
||||
|
||||
- Many dependencies are updated. Please see `package.json`.
|
||||
- This is the hardest part of this update. I read most of the changes in the dependencies. If you find any extra information, please let me know.
|
||||
- As upgrading TypeScript, Fixed many UInt8Array<ArrayBuffer> and Uint8Array type mismatches.
|
||||
-
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Sending configuration via Peer-to-Peer connection is not compatible with older versions.
|
||||
- Please upgrade all devices to v0.25.24.beta1 or later to use this feature again.
|
||||
- This is due to security improvements in the encryption scheme.
|
||||
|
||||
## 0.25.23
|
||||
|
||||
26th October, 2025
|
||||
|
||||
The next version we are preparing (you know that as 0.25.23.beta1) is now still on beta, resulting in this rather unfortunate versioning situation. Apologies for the confusion. The next v0.25.23.beta2 will be v0.25.24.beta1. In other words, this is a v0.25.22.patch-1 actually, but possibly not allowed by Obsidian's rule.
|
||||
(Perhaps we ought to declare 1.0.0 with a little more confidence. The current minor part has been effectively a major one for a long time. If it were 1.22.1 and 1.23.0.beta1, no confusion ).
|
||||
|
||||
### Fixed
|
||||
|
||||
- We are now able to enable optional features correctly again (#732).
|
||||
- No longer oversized files have been processed, furthermore.
|
||||
|
||||
- Before creating a chunk, the file is verified as the target.
|
||||
- The behaviour upon receiving replication has been changed as follows:
|
||||
- If the remote file is oversized, it is ignored.
|
||||
- If not, but while the local file is oversized, it is also ignored.
|
||||
|
||||
- We are now able to enable optional features correctly again (#732).
|
||||
- No longer oversized files have been processed, furthermore.
|
||||
- Before creating a chunk, the file is verified as the target.
|
||||
- The behaviour upon receiving replication has been changed as follows:
|
||||
- If the remote file is oversized, it is ignored.
|
||||
- If not, but while the local file is oversized, it is also ignored.
|
||||
|
||||
## 0.25.22
|
||||
|
||||
15th October, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug that caused wrong event bindings and flag inversion (#727)
|
||||
- This caused following issues:
|
||||
- In some cases, settings changes were not applied or saved correctly.
|
||||
- Automatic synchronisation did not begin correctly.
|
||||
|
||||
### Improved
|
||||
|
||||
- Too large diffs are not shown in the file comparison view, due to performance reasons.
|
||||
|
||||
### Notes
|
||||
|
||||
- The checking algorithm implemented in 0.25.20 is also raised as PR (#237). And completely I merged it manually.
|
||||
- Sorry for lacking merging this PR, and let me say thanks to the great contribution, @bioluks !
|
||||
- Known issues:
|
||||
- Sync on Editor save seems not to work correctly in some cases.
|
||||
- I am investigating this issue. If you have any information, please let me know.
|
||||
|
||||
## 0.25.21
|
||||
|
||||
|
||||
Reference in New Issue
Block a user