From 6afb828bd9c33750b616cd10805ef5d59baedc54 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Tue, 26 Aug 2025 21:06:22 +0100 Subject: [PATCH 1/5] Fix CI --- scripts/check.sh | 2 -- scripts/e2e.sh | 13 +++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/check.sh b/scripts/check.sh index 750831ed..f807d2c8 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -9,8 +9,6 @@ cargo fmt --all -- --check cargo machete cargo test --verbose -scripts/update-api-types.sh - echo "Running checks in frontend" cd ../frontend npm ci diff --git a/scripts/e2e.sh b/scripts/e2e.sh index 55cb1ac8..821bd0bc 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -3,6 +3,12 @@ set -e set -o pipefail +node_version=$(node -v | sed 's/^v\([0-9]*\).*/\1/') +if [ "$node_version" != "22" ]; then + echo "Error: This script requires Node.js version 22, found: $node_version" + exit 1 +fi + # Check if the argument is provided if [ $# -eq 0 ]; then echo "Usage: $0 " @@ -20,6 +26,13 @@ npm run build ../scripts/utils/wait-for-server.sh +../scripts/update-api-types.sh +if [[ $(git status --porcelain) ]]; then + git status --porcelain + echo "Failing CI because the working directory is not clean after generating api types" + exit 1 +fi + pids=() for i in $(seq 1 $process_count); do node test-client/dist/cli.js > "../logs/log_${i}.log" 2>&1 & -- 2.47.2 From 2ff1384fde1db5c3051ff8e4e39764452ddf64b3 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Tue, 26 Aug 2025 21:17:57 +0100 Subject: [PATCH 2/5] Fix cursor moving perf --- .../views/cursors/remote-cursors-plugin.ts | 76 +++++++++++-------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts b/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts index 5dff2c59..04669a16 100644 --- a/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts +++ b/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts @@ -102,39 +102,11 @@ export class RemoteCursorsPluginValue implements PluginValue { const original = update.startState.doc.toString(); const edited = update.state.doc.toString(); - const updatedPositions: number[] = []; - const reconciled = reconcileWithHistory( + RemoteCursorsPluginValue.interpolateRemoteCursorPositions( original, - { - text: original, - cursors: RemoteCursorsPluginValue.cursors.flatMap( - ({ span }, i) => [ - { id: i * 2, position: span.start }, - { id: i * 2 + 1, position: span.end } - ] - ) - }, - edited, - "Character" + edited ); - reconciled.cursors.forEach(({ id, position }) => { - const whereToJump = findWhereToMoveCursor( - position, - reconciled.history - ); - if (whereToJump !== null) { - updatedPositions[id] = whereToJump; - } else { - updatedPositions[id] = position; - } - }); - - RemoteCursorsPluginValue.cursors.forEach(({ span }, i) => { - span.start = updatedPositions[i * 2]; - span.end = updatedPositions[i * 2 + 1]; - }); - const decorations: Range[] = []; RemoteCursorsPluginValue.cursors.forEach( @@ -207,6 +179,50 @@ export class RemoteCursorsPluginValue implements PluginValue { this.decorations = Decoration.set(decorations, true); } + + private static interpolateRemoteCursorPositions( + original: string, + edited: string + ): void { + if ( + original === edited || + RemoteCursorsPluginValue.cursors.length === 0 + ) { + return; + } + + const updatedPositions: number[] = []; + const reconciled = reconcileWithHistory( + original, + { + text: original, + cursors: RemoteCursorsPluginValue.cursors.flatMap( + ({ span }, i) => [ + { id: i * 2, position: span.start }, + { id: i * 2 + 1, position: span.end } + ] + ) + }, + edited + ); + + reconciled.cursors.forEach(({ id, position }) => { + const whereToJump = findWhereToMoveCursor( + position, + reconciled.history + ); + if (whereToJump !== null) { + updatedPositions[id] = whereToJump; + } else { + updatedPositions[id] = position; + } + }); + + RemoteCursorsPluginValue.cursors.forEach(({ span }, i) => { + span.start = updatedPositions[i * 2]; + span.end = updatedPositions[i * 2 + 1]; + }); + } } export const remoteCursorsPlugin = ViewPlugin.fromClass( -- 2.47.2 From d6e4305588693c13a653aa04845357748172621c Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Tue, 26 Aug 2025 21:22:18 +0100 Subject: [PATCH 3/5] Fix lint --- .../views/cursors/remote-cursors-plugin.ts | 126 +++++++++--------- scripts/e2e.sh | 6 +- 2 files changed, 67 insertions(+), 65 deletions(-) diff --git a/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts b/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts index 04669a16..8801ecda 100644 --- a/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts +++ b/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts @@ -18,25 +18,6 @@ import { getRandomColor } from "src/utils/get-random-color"; import type { SpanWithHistory } from "reconcile-text"; import { reconcileWithHistory } from "reconcile-text"; -function findWhereToMoveCursor( - cursor: number, - spans: SpanWithHistory[] -): number | null { - let position = 0; - for (const span of spans) { - // left and origin are the same - if (position === cursor && span.history === "AddedFromRight") { - return position + span.text.length; - } - position += span.text.length; - if (position === cursor && span.history === "RemovedFromRight") { - return position - span.text.length; - } - } - - return null; -} - const forceUpdate = StateEffect.define(); export class RemoteCursorsPluginValue implements PluginValue { @@ -98,6 +79,69 @@ export class RemoteCursorsPluginValue implements PluginValue { }); } + private static interpolateRemoteCursorPositions( + original: string, + edited: string + ): void { + if ( + original === edited || + RemoteCursorsPluginValue.cursors.length === 0 + ) { + return; + } + + const updatedPositions: number[] = []; + const reconciled = reconcileWithHistory( + original, + { + text: original, + cursors: RemoteCursorsPluginValue.cursors.flatMap( + ({ span }, i) => [ + { id: i * 2, position: span.start }, + { id: i * 2 + 1, position: span.end } + ] + ) + }, + edited + ); + + reconciled.cursors.forEach(({ id, position }) => { + const whereToJump = RemoteCursorsPluginValue.findWhereToMoveCursor( + position, + reconciled.history + ); + if (whereToJump !== null) { + updatedPositions[id] = whereToJump; + } else { + updatedPositions[id] = position; + } + }); + + RemoteCursorsPluginValue.cursors.forEach(({ span }, i) => { + span.start = updatedPositions[i * 2]; + span.end = updatedPositions[i * 2 + 1]; + }); + } + + private static findWhereToMoveCursor( + cursor: number, + spans: SpanWithHistory[] + ): number | null { + let position = 0; + for (const span of spans) { + // left and origin are the same + if (position === cursor && span.history === "AddedFromRight") { + return position + span.text.length; + } + position += span.text.length; + if (position === cursor && span.history === "RemovedFromRight") { + return position - span.text.length; + } + } + + return null; + } + public update(update: ViewUpdate): void { const original = update.startState.doc.toString(); const edited = update.state.doc.toString(); @@ -179,50 +223,6 @@ export class RemoteCursorsPluginValue implements PluginValue { this.decorations = Decoration.set(decorations, true); } - - private static interpolateRemoteCursorPositions( - original: string, - edited: string - ): void { - if ( - original === edited || - RemoteCursorsPluginValue.cursors.length === 0 - ) { - return; - } - - const updatedPositions: number[] = []; - const reconciled = reconcileWithHistory( - original, - { - text: original, - cursors: RemoteCursorsPluginValue.cursors.flatMap( - ({ span }, i) => [ - { id: i * 2, position: span.start }, - { id: i * 2 + 1, position: span.end } - ] - ) - }, - edited - ); - - reconciled.cursors.forEach(({ id, position }) => { - const whereToJump = findWhereToMoveCursor( - position, - reconciled.history - ); - if (whereToJump !== null) { - updatedPositions[id] = whereToJump; - } else { - updatedPositions[id] = position; - } - }); - - RemoteCursorsPluginValue.cursors.forEach(({ span }, i) => { - span.start = updatedPositions[i * 2]; - span.end = updatedPositions[i * 2 + 1]; - }); - } } export const remoteCursorsPlugin = ViewPlugin.fromClass( diff --git a/scripts/e2e.sh b/scripts/e2e.sh index 821bd0bc..952e1855 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -26,12 +26,14 @@ npm run build ../scripts/utils/wait-for-server.sh -../scripts/update-api-types.sh +cd .. +scripts/update-api-types.sh if [[ $(git status --porcelain) ]]; then git status --porcelain echo "Failing CI because the working directory is not clean after generating api types" exit 1 fi +cd frontend pids=() for i in $(seq 1 $process_count); do @@ -39,7 +41,7 @@ for i in $(seq 1 $process_count); do pids+=($!) done -cd - +cd .. print_failed_log() { for i in $(seq 1 $process_count); do -- 2.47.2 From d513ad9824aa105767bab3867e439e05e084ae45 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Tue, 26 Aug 2025 22:23:05 +0100 Subject: [PATCH 4/5] Bump versions to 0.6.1 --- frontend/obsidian-plugin/manifest.json | 2 +- frontend/obsidian-plugin/package.json | 2 +- frontend/package-lock.json | 6 +++--- frontend/sync-client/package.json | 2 +- frontend/test-client/package.json | 2 +- manifest.json | 2 +- sync-server/Cargo.lock | 2 +- sync-server/Cargo.toml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/obsidian-plugin/manifest.json b/frontend/obsidian-plugin/manifest.json index c4464e91..a15afeab 100644 --- a/frontend/obsidian-plugin/manifest.json +++ b/frontend/obsidian-plugin/manifest.json @@ -1,7 +1,7 @@ { "id": "vault-link", "name": "VaultLink", - "version": "0.6.0", + "version": "0.6.1", "minAppVersion": "0.0.0", "description": "Self-hosted synchronization and collaboration for your Vault.", "author": "Andras Schmelczer", diff --git a/frontend/obsidian-plugin/package.json b/frontend/obsidian-plugin/package.json index 0bd87b36..ce8f8b1a 100644 --- a/frontend/obsidian-plugin/package.json +++ b/frontend/obsidian-plugin/package.json @@ -1,6 +1,6 @@ { "name": "vault-link-obsidian-plugin", - "version": "0.6.0", + "version": "0.6.1", "description": "This is a sample plugin for Obsidian (https://obsidian.md)", "main": "main.js", "scripts": { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 54efe68d..73309f06 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -6361,7 +6361,7 @@ }, "obsidian-plugin": { "name": "vault-link-obsidian-plugin", - "version": "0.6.0", + "version": "0.6.1", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.14", @@ -6390,7 +6390,7 @@ } }, "sync-client": { - "version": "0.6.0", + "version": "0.6.1", "dependencies": { "byte-base64": "^1.1.0", "minimatch": "^10.0.1", @@ -6433,7 +6433,7 @@ } }, "test-client": { - "version": "0.6.0", + "version": "0.6.1", "bin": { "test-client": "dist/cli.js" }, diff --git a/frontend/sync-client/package.json b/frontend/sync-client/package.json index 1d32c118..b44ac8cc 100644 --- a/frontend/sync-client/package.json +++ b/frontend/sync-client/package.json @@ -1,6 +1,6 @@ { "name": "sync-client", - "version": "0.6.0", + "version": "0.6.1", "main": "dist/sync-client.node.js", "browser": "dist/sync-client.web.js", "types": "dist/types/index.d.ts", diff --git a/frontend/test-client/package.json b/frontend/test-client/package.json index 0c8e69ea..dc5d2d44 100644 --- a/frontend/test-client/package.json +++ b/frontend/test-client/package.json @@ -1,6 +1,6 @@ { "name": "test-client", - "version": "0.6.0", + "version": "0.6.1", "private": true, "bin": { "test-client": "./dist/cli.js" diff --git a/manifest.json b/manifest.json index c4464e91..a15afeab 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "vault-link", "name": "VaultLink", - "version": "0.6.0", + "version": "0.6.1", "minAppVersion": "0.0.0", "description": "Self-hosted synchronization and collaboration for your Vault.", "author": "Andras Schmelczer", diff --git a/sync-server/Cargo.lock b/sync-server/Cargo.lock index 9e9574d7..7bcdcb6b 100644 --- a/sync-server/Cargo.lock +++ b/sync-server/Cargo.lock @@ -2205,7 +2205,7 @@ dependencies = [ [[package]] name = "sync_server" -version = "0.6.0" +version = "0.6.1" dependencies = [ "anyhow", "axum", diff --git a/sync-server/Cargo.toml b/sync-server/Cargo.toml index b4e5b1f8..7565adde 100644 --- a/sync-server/Cargo.toml +++ b/sync-server/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andras Schmelczer "] edition = "2024" license = "MIT" repository = "https://github.com/schmelczer/vault-link" -version = "0.6.0" +version = "0.6.1" [dependencies] serde = { version = "1.0.219", default-features = false, features = ["derive"] } -- 2.47.2 From 0d6a2ceb5afeca450ed067fb8a209a3b1d8b9b03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 00:08:24 +0000 Subject: [PATCH 5/5] Bump sass from 1.89.1 to 1.91.0 in /frontend Bumps [sass](https://github.com/sass/dart-sass) from 1.89.1 to 1.91.0. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.89.1...1.91.0) --- updated-dependencies: - dependency-name: sass dependency-version: 1.91.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- frontend/obsidian-plugin/package.json | 2 +- frontend/package-lock.json | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/obsidian-plugin/package.json b/frontend/obsidian-plugin/package.json index 0bd87b36..18159d4b 100644 --- a/frontend/obsidian-plugin/package.json +++ b/frontend/obsidian-plugin/package.json @@ -23,7 +23,7 @@ "mini-css-extract-plugin": "^2.9.2", "obsidian": "1.8.7", "resolve-url-loader": "^5.0.0", - "sass": "^1.89.1", + "sass": "^1.91.0", "sass-loader": "^16.0.5", "sync-client": "file:../sync-client", "terser-webpack-plugin": "^5.3.14", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 54efe68d..4d8daf6f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -5190,7 +5190,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.89.1", + "version": "1.91.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.91.0.tgz", + "integrity": "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA==", "dev": true, "license": "MIT", "dependencies": { @@ -6375,7 +6377,7 @@ "obsidian": "1.8.7", "reconcile-text": "^0.5.0", "resolve-url-loader": "^5.0.0", - "sass": "^1.89.1", + "sass": "^1.91.0", "sass-loader": "^16.0.5", "sync-client": "file:../sync-client", "terser-webpack-plugin": "^5.3.14", -- 2.47.2