diff --git a/.forgejo/workflows/android.yml b/.forgejo/workflows/android.yml deleted file mode 100644 index ec54ba6..0000000 --- a/.forgejo/workflows/android.yml +++ /dev/null @@ -1,97 +0,0 @@ -# Android (Tauri v2 mobile) — SEPARATE from desktop.yml because it is a different -# surface with a different toolchain, not a different bundle of the same one. The -# Rust core is cross-compiled with the NDK and then packaged by the Gradle project -# in desktop/src-tauri/gen/android. -# -# Toolchain comes from the ci-tauri-android image (Rust + the Android ABIs + -# SDK/NDK + JDK 17 + tauri-cli); runs-on is only a scheduling label (Label Model B). -# -# Android, desktop and web are peer surfaces on one quality bar, so this triggers on -# the same shared frontend that desktop.yml does — the frontend is compiled INTO the -# app by generate_context!, so a frontend commit that skipped this lane would ship a -# stale phone build. -name: Android (Tauri) - -on: - push: - branches: [dev, main] - tags: ["v*"] - paths: - - "desktop/**" - - "frontend/**" - - ".forgejo/workflows/android.yml" - workflow_dispatch: - -concurrency: - group: android-${{ github.ref }} - cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} - -permissions: - contents: read - -jobs: - build: - name: Android APK (debug) - runs-on: python-ci - container: - image: git.fabledsword.com/bvandeusen/ci-tauri-android:1.97 - steps: - - uses: actions/checkout@v6 - - # Same reason as the desktop lanes: generate_context! embeds the built - # frontend at compile time, so it must exist before cargo runs. - - name: Build the shared frontend - run: npm ci && npm run build - working-directory: frontend - - # The lockfile gate, matching the desktop lanes (issue 2102). Fetching for the - # Android target also pre-warms exactly the crates the build will want. - - name: Verify the lockfile and fetch dependencies - run: cargo fetch --locked --target aarch64-linux-android - working-directory: desktop/src-tauri - - # DEBUG, and arm64 only, deliberately. - # - # Debug because a release APK has to be signed, and the release keystore must - # be generated by the operator and never pass through CI logs or an agent - # session (the constraint recorded on task 2136 for the updater key applies - # identically here). Gradle's throwaway debug keystore needs nothing from - # anyone, so this lane can prove the app COMPILES and PACKAGES today and grow - # a signed release job when a keystore exists. - # - # arm64 + x86_64. arm64 is every real device; x86_64 is what a desktop - # emulator runs, and an arm64-only APK installs there and then dies unable to - # load its native library — so a build nobody can try on an emulator is a - # build nobody checks. armv7 and i686 are left out: 32-bit hardware we do not - # target, and the image carries all four if that changes. - - name: Tauri build (Android APK) - run: | - version="$(sh ../packaging/build-version.sh)" - echo "Building version $version" - cargo tauri android build \ - --debug \ - --target aarch64 \ - --target x86_64 \ - --config '{"build":{"beforeBuildCommand":""}}' \ - --config "{\"version\":\"$version\"}" - working-directory: desktop/src-tauri - - # Located rather than hardcoded: the output path depends on the target and - # build type, and a wrong literal here would fail as "no files found" long - # after the expensive step succeeded. - - name: Locate the APK - run: | - apk="$(find desktop/src-tauri/gen/android/app/build/outputs/apk -name '*.apk' -type f | head -1)" - [ -n "$apk" ] || { echo "ERROR: no APK produced" >&2; exit 1; } - echo "Built $apk ($(du -h "$apk" | cut -f1))" - echo "APK_PATH=$apk" >> "$GITHUB_ENV" - - # Mirrored action, never actions/upload-artifact — see desktop.yml's Upload - # bundles step for the full reasoning. Pinned by SHA because the mirror - # auto-syncs. - - name: Upload the APK - uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245 - with: - name: thoughtsync-android-debug - path: ${{ env.APK_PATH }} - if-no-files-found: error diff --git a/.forgejo/workflows/desktop.yml b/.forgejo/workflows/desktop.yml index 1f29cf8..c557864 100644 --- a/.forgejo/workflows/desktop.yml +++ b/.forgejo/workflows/desktop.yml @@ -20,6 +20,13 @@ on: tags: ["v*"] paths: - "desktop/**" + # The shared client core (store + sync engine) the desktop wraps. Its own + # crate since the Android client binds the same code, so a change there is a + # change to this app even though nothing under desktop/ moved. + - "core/**" + # The workspace manifest and lockfile, which now live at the repo root. + - "Cargo.toml" + - "Cargo.lock" # The whole frontend, not just the adapter/bridge seam: it is compiled INTO # the desktop binary, so any part of it changing means the shipped app is out # of date. Config and lockfile included — a dependency bump changes the bundle @@ -142,7 +149,6 @@ jobs: cargo tauri signer sign "$appimage" [ -s "$appimage.sig" ] || { echo "ERROR: re-signing produced no .sig" >&2; exit 1; } echo "Re-signed $(basename "$appimage")" - working-directory: desktop/src-tauri # install.sh hands the .deb to every Debian/Ubuntu user, so the package's # Depends must be right BEFORE a release exists. Prints the generated @@ -180,9 +186,9 @@ jobs: with: name: thoughtsync-linux path: | - desktop/src-tauri/target/release/bundle/appimage/*.AppImage - desktop/src-tauri/target/release/bundle/deb/*.deb - desktop/src-tauri/target/release/bundle/arch/*.pkg.tar.* + target/release/bundle/appimage/*.AppImage + target/release/bundle/deb/*.deb + target/release/bundle/arch/*.pkg.tar.* # error, not warn: a build that bundles nothing should report as a # failure, not as a green run with an empty artifact. if-no-files-found: error @@ -296,7 +302,7 @@ jobs: uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245 with: name: thoughtsync-windows - path: desktop/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe + path: target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe if-no-files-found: error # Publishes to the SAME release as the Linux job. Safe to run twice: the diff --git a/.gitignore b/.gitignore index 36b13f1..46a9c41 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,6 @@ cython_debug/ # PyPI configuration file .pypirc + +# Rust workspace build output (one target dir for core + desktop + android) +/target/ diff --git a/desktop/src-tauri/Cargo.lock b/Cargo.lock similarity index 99% rename from desktop/src-tauri/Cargo.lock rename to Cargo.lock index 276f510..60fadd5 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/Cargo.lock @@ -3946,7 +3946,7 @@ dependencies = [ ] [[package]] -name = "thoughtsync-desktop" +name = "thoughtsync-core" version = "0.1.0" dependencies = [ "chrono", @@ -3957,11 +3957,21 @@ dependencies = [ "serde", "serde_json", "sha2", + "uuid", +] + +[[package]] +name = "thoughtsync-desktop" +version = "0.1.0" +dependencies = [ + "log", + "serde", + "serde_json", "tauri", "tauri-build", "tauri-plugin-log", "tauri-plugin-updater", - "uuid", + "thoughtsync-core", ] [[package]] @@ -5294,9 +5304,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47402523226a02bfe5230160dc3ccc089aa6f6f19e7fcbb4e6f824bbb1b4aa62" +checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..628329b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +# Rust workspace. Two members today: the framework-free client core, and the Tauri +# desktop app that wraps it. The Android client becomes a third consumer of `core` +# through uniffi (Scribe note 2730) — which is the reason the core is a crate at all +# rather than a module inside the desktop app. +[workspace] +resolver = "2" +members = ["core", "desktop/src-tauri"] + +# Shared pins, so two consumers of the core cannot drift onto different versions of +# the same dependency and resolve differently. +[workspace.dependencies] +serde = { version = "1", features = ["derive"] } +serde_json = "1" +log = "0.4" + +# Tauri's default release profile: smaller, faster shipped binaries. +# +# At the WORKSPACE root, not in the desktop member: cargo ignores profiles declared +# by a non-root package, so leaving it there would silently drop lto/strip/opt-level +# from every release build with only a warning to say so. +[profile.release] +codegen-units = 1 +lto = true +opt-level = "s" +panic = "abort" +strip = true diff --git a/ci-requirements.md b/ci-requirements.md index 69989f9..a443498 100644 --- a/ci-requirements.md +++ b/ci-requirements.md @@ -159,49 +159,14 @@ backend/frontend push. - No Postgres lane (unchanged): the desktop app's local store + sync behavior is verified on the operator's machine, not in CI. -## Android (Tauri mobile) lane — `android.yml`, third image +## Android lane — REMOVED 2026-08-17 -Android is a peer surface, not a desktop variant, so it gets its own workflow. - -- **Image:** `git.fabledsword.com/bvandeusen/ci-tauri-android:1.97` (Rust + the - four Android ABIs + Android SDK/NDK + JDK 17 + Node + tauri-cli). Pins held in - lockstep with `ci-tauri` and `ci-tauri-win` — all three compile the same crate. -- **Why a third image.** `ci-android` says in its own header that it excludes the - NDK ("No Flutter, no NDK, no CMake") and carries no Rust; `ci-tauri` has Rust - but no SDK, plus a WebKitGTK stack Android never touches. Tauri needs the NDK - precisely because the core is Rust compiled to Android ABIs. -- **JDK 17, not the 25 `ci-android`/`ci-flutter` ship.** `tauri android init` - GENERATES the Gradle project, so its version isn't ours to choose — it produced - Gradle **8.14.3** + AGP **8.11.0** here. `CI-android/versions.env` records that - JDK 25 needs Gradle 9.1.0+ and that anything older fails with an opaque - `"25.0.3"` message. Bumping this JDK without checking what Tauri templates will - break the build with an error that names nothing useful. -- **`gen/android/` is COMMITTED.** `tauri android init` generates it, and the only - ignores are its own (build outputs, `local.properties`, `key.properties`, - `keystore.properties`). CI must not have to regenerate it, and manifest or - Gradle edits have to survive. -- **Debug APK only, arm64 only, today.** A release APK must be signed, and the - keystore has to be generated by the operator and never pass through CI logs or - an agent session — the same constraint recorded for the updater key (task 2136). - Gradle's throwaway debug keystore needs nothing from anyone, so this lane proves - the app compiles and packages. arm64 is every real device; the image carries the - other three ABIs, so widening is a one-word change. -- **Green means it BUILT.** Like the Windows lane, a Linux runner cannot execute - the artifact. Nothing here proves the app runs, renders, or is usable by finger. -- **TLS: OpenSSL is vendored on Android.** `reqwest` is pinned to `native-tls`, - which is deliberate for the Windows lane (it resolves to `schannel`, keeping - C/asm out of the cross-compile). On Android it resolves to **OpenSSL**, and the - first build duly failed with `openssl-sys`: *"Could not find directory of - OpenSSL installation"* — there is no Android OpenSSL to link against. - `Cargo.toml` now carries a `cfg(target_os = "android")` block enabling - `openssl-sys`'s `vendored` feature, which compiles OpenSSL from source with the - NDK toolchain. That is why the image ships `perl` + `make`. -- **Why not `rustls` on Android.** It builds faster and was the obvious fix, but - rustls ships its own root store — the phone would trust a DIFFERENT set of - certificates than the desktop. A self-hosted server behind a private or - enterprise CA would then work on one surface and fail on another. One TLS stack - across all surfaces is worth more than the build minutes. Revisit only if - vendored OpenSSL becomes the thing that breaks this lane repeatedly. +The Tauri-mobile Android lane is gone. Android is a native Kotlin/Compose client +over the shared `thoughtsync-core` crate instead — see Scribe note 2730 for the +decision and milestone M12 for the arc. Its CI lane and image will be rebuilt on +that basis; `ci-tauri-android` is being repurposed (drop `tauri-cli`, add +`cargo-ndk`) rather than deleted, because its NDK binutils symlinks and PATH fix +are load-bearing for any Rust-on-Android cross-compile. ## Formatting the Rust lane before pushing @@ -212,8 +177,8 @@ a formatter is neither a test run nor a local stack: ``` docker run --rm --user "$(id -u):$(id -g)" -e CARGO_HOME=/tmp/cargo \ - -v "$PWD/desktop/src-tauri:/w" -w /w \ - git.fabledsword.com/bvandeusen/ci-tauri:1.97 cargo fmt --check + -v "$PWD:/w" -w /w \ + git.fabledsword.com/bvandeusen/ci-tauri:1.97 cargo fmt --all --check ``` Drop `--check` to apply. `--user` keeps the container from leaving root-owned @@ -225,7 +190,7 @@ literal — copying that shape caused one of the four failures. ## The desktop lockfile -`desktop/src-tauri/Cargo.lock` is **committed**, per Cargo's own guidance for +`Cargo.lock` is **committed** at the workspace root, per Cargo's own guidance for binary crates. Without it every CI run re-resolved the graph, which meant a released `.deb`/`.AppImage`/`.exe` couldn't be rebuilt from its tag, a build could break with no repo change, and Renovate had nothing to bump (issue 2102). @@ -244,7 +209,7 @@ above, and resolution is neither a test run nor a build: ``` docker run --rm --user "$(id -u):$(id -g)" -e CARGO_HOME=/tmp/cargo \ - -v "$PWD/desktop/src-tauri:/w" -w /w \ + -v "$PWD:/w" -w /w \ git.fabledsword.com/bvandeusen/ci-tauri:1.97 cargo generate-lockfile ``` diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..bdc1b87 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "thoughtsync-core" +version = "0.1.0" +description = "ThoughtSync client core — local-first SQLite store and opt-in sync engine" +authors = ["bvandeusen"] +edition = "2021" + +[dependencies] +serde = { workspace = true } +serde_json = { workspace = true } +log = { workspace = true } +# Local-first store (M10.4): bundled = compile SQLite in, so there's no system +# libsqlite dependency to vary across the AppImage / native / Windows / Android builds. +rusqlite = { version = "0.32", features = ["bundled"] } +uuid = { version = "1", features = ["v4"] } +# RFC3339 timestamps for created_at/updated_at/remind_at (Date.parse-able on the JS side). +chrono = { version = "0.4", default-features = false, features = ["clock"] } +# HTTP for the opt-in server handshake (M10.6) and the sync engine (M10.7). +# +# native-tls, NOT rustls, deliberately: on x86_64-pc-windows-msvc native-tls resolves +# to `schannel` — pure-Rust bindings to the OS TLS stack — so nothing C or assembly +# has to cross-compile on the Windows lane, which is the fragile one. rustls would +# instead pull in ring/aws-lc-rs and their assembler. On Linux native-tls uses +# OpenSSL, whose headers (libssl-dev) ci-tauri already ships. +# default-features off drops http2/charset we don't need for a JSON API. +reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] } +# Verifying downloaded attachment bytes against the sha256 the server advertised. +sha2 = "0.10" + +# Android has no system OpenSSL to link against, and `native-tls` resolves to +# OpenSSL there — unlike Windows, where it lands on schannel and costs nothing. +# Without this the build dies at `openssl-sys`: "Could not find directory of +# OpenSSL installation". +# +# `vendored` compiles OpenSSL from source with the NDK toolchain. The alternative +# was rustls on Android only, which builds faster — but rustls ships its own root +# store, so the phone would trust a DIFFERENT set of certificates than the desktop +# does. A self-hosted server behind a private or enterprise CA would then work on +# one surface and fail on another, and "the surfaces behave the same" is worth more +# than build minutes. +# +# Declared as a direct dependency purely to turn the feature on: cargo's feature +# unification applies it to the copy `native-tls` pulls in transitively. +[target.'cfg(target_os = "android")'.dependencies] +openssl-sys = { version = "0.9", features = ["vendored"] } diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 0000000..9f1873f --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,14 @@ +//! ThoughtSync's client core: the on-device SQLite store and the sync engine. +//! +//! Deliberately free of any UI framework. The desktop wraps it in Tauri commands; +//! the Android client binds it through uniffi. Neither owns it, and a change to +//! either must not require touching this crate — that separation is the whole point +//! (see Scribe note 2730). It was already true before the split: every file here +//! carried zero Tauri references, which is what made the extraction a move rather +//! than a rewrite. +//! +//! - `local` — the source of truth. Works with no server and no account. +//! - `sync` — entirely opt-in. Nothing in it runs until a server is linked. + +pub mod local; +pub mod sync; diff --git a/desktop/src-tauri/src/local/derive.rs b/core/src/local/derive.rs similarity index 100% rename from desktop/src-tauri/src/local/derive.rs rename to core/src/local/derive.rs diff --git a/desktop/src-tauri/src/local/mod.rs b/core/src/local/mod.rs similarity index 60% rename from desktop/src-tauri/src/local/mod.rs rename to core/src/local/mod.rs index ed8e7a7..44d308f 100644 --- a/desktop/src-tauri/src/local/mod.rs +++ b/core/src/local/mod.rs @@ -1,8 +1,9 @@ -//! The local-first core: an on-device SQLite store + Tauri commands implementing the -//! frontend's repository seam, so the desktop app is fully usable with no server and -//! no login. adapters/local.ts (M10.5) calls into `commands`. +//! The local-first store: on-device SQLite, and the source of truth for every +//! client. A client built on this is fully usable with no server and no account. +//! +//! Framework-free on purpose. The desktop reaches it through Tauri commands and +//! Android through uniffi, but neither of those concerns appears in here. -pub mod commands; pub mod derive; pub mod models; pub mod retention; @@ -14,9 +15,10 @@ use std::sync::Mutex; use rusqlite::Connection; -/// The shared database handle, held in Tauri's managed state. rusqlite connections -/// aren't `Sync`, so a `Mutex` serializes access (fine — operations are quick and the -/// UI is single-user). +/// The shared database handle. rusqlite connections aren't `Sync`, so a `Mutex` +/// serializes access — fine, since operations are quick and a client is single-user. +/// How it is held is the caller's business: Tauri manages it as state, Android holds +/// it in the uniffi object. pub struct Db(pub Mutex); /// Open (creating if needed) the database at `path` and bring it to the latest schema. diff --git a/desktop/src-tauri/src/local/models.rs b/core/src/local/models.rs similarity index 100% rename from desktop/src-tauri/src/local/models.rs rename to core/src/local/models.rs diff --git a/desktop/src-tauri/src/local/retention.rs b/core/src/local/retention.rs similarity index 100% rename from desktop/src-tauri/src/local/retention.rs rename to core/src/local/retention.rs diff --git a/desktop/src-tauri/src/local/schema.rs b/core/src/local/schema.rs similarity index 100% rename from desktop/src-tauri/src/local/schema.rs rename to core/src/local/schema.rs diff --git a/desktop/src-tauri/src/local/store.rs b/core/src/local/store.rs similarity index 100% rename from desktop/src-tauri/src/local/store.rs rename to core/src/local/store.rs diff --git a/desktop/src-tauri/src/sync/blobs.rs b/core/src/sync/blobs.rs similarity index 100% rename from desktop/src-tauri/src/sync/blobs.rs rename to core/src/sync/blobs.rs diff --git a/desktop/src-tauri/src/sync/client.rs b/core/src/sync/client.rs similarity index 100% rename from desktop/src-tauri/src/sync/client.rs rename to core/src/sync/client.rs diff --git a/desktop/src-tauri/src/sync/compat.rs b/core/src/sync/compat.rs similarity index 100% rename from desktop/src-tauri/src/sync/compat.rs rename to core/src/sync/compat.rs diff --git a/desktop/src-tauri/src/sync/engine.rs b/core/src/sync/engine.rs similarity index 100% rename from desktop/src-tauri/src/sync/engine.rs rename to core/src/sync/engine.rs diff --git a/desktop/src-tauri/src/sync/mod.rs b/core/src/sync/mod.rs similarity index 74% rename from desktop/src-tauri/src/sync/mod.rs rename to core/src/sync/mod.rs index dc78966..1ab89c9 100644 --- a/desktop/src-tauri/src/sync/mod.rs +++ b/core/src/sync/mod.rs @@ -7,14 +7,13 @@ //! be talked to at all. Pure decision logic, no I/O. //! - `client` — HTTP transport: the handshake call and device-token auth. //! - `state` — the persisted link record (server, token, change-feed cursor). -//! - `commands` — the Tauri surface the Settings UI drives. +//! - `engine` — one full cycle: push local changes, then pull the server's. //! -//! The engine that moves notes — push, pull, last-write-wins — lands in M10.7b/c and -//! consults `compat` before it does anything. +//! The UI surface that drives this lives in whichever client is wrapping the crate, +//! not here. pub mod blobs; pub mod client; -pub mod commands; pub mod compat; pub mod engine; pub mod pull; diff --git a/desktop/src-tauri/src/sync/pull.rs b/core/src/sync/pull.rs similarity index 100% rename from desktop/src-tauri/src/sync/pull.rs rename to core/src/sync/pull.rs diff --git a/desktop/src-tauri/src/sync/push.rs b/core/src/sync/push.rs similarity index 100% rename from desktop/src-tauri/src/sync/push.rs rename to core/src/sync/push.rs diff --git a/desktop/src-tauri/src/sync/state.rs b/core/src/sync/state.rs similarity index 100% rename from desktop/src-tauri/src/sync/state.rs rename to core/src/sync/state.rs diff --git a/desktop/src-tauri/src/sync/wire.rs b/core/src/sync/wire.rs similarity index 100% rename from desktop/src-tauri/src/sync/wire.rs rename to core/src/sync/wire.rs diff --git a/desktop/packaging/appimage/debundle-graphics.sh b/desktop/packaging/appimage/debundle-graphics.sh index 8ad247f..d993feb 100755 --- a/desktop/packaging/appimage/debundle-graphics.sh +++ b/desktop/packaging/appimage/debundle-graphics.sh @@ -20,7 +20,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" -BUNDLE_DIR="$REPO_ROOT/desktop/src-tauri/target/release/bundle/appimage" +BUNDLE_DIR="$REPO_ROOT/target/release/bundle/appimage" # appimagetool is published only under the rolling "continuous" tag (the project # cuts no semver releases), so this URL is the pinned distribution channel. @@ -90,7 +90,7 @@ fi echo "==> Locating appimagetool" # Prefer the copy Tauri already downloaded during the build (no network, and # version-matched to the toolchain that produced the AppImage). -APPIMAGETOOL="$(find "$REPO_ROOT/desktop/src-tauri/target" -name 'appimagetool-*.AppImage' -type f 2>/dev/null | head -n1 || true)" +APPIMAGETOOL="$(find "$REPO_ROOT/target" -name 'appimagetool-*.AppImage' -type f 2>/dev/null | head -n1 || true)" if [ -z "${APPIMAGETOOL:-}" ]; then echo " not cached by Tauri; downloading from continuous channel" APPIMAGETOOL="$WORK/appimagetool" diff --git a/desktop/packaging/arch/package-prebuilt.sh b/desktop/packaging/arch/package-prebuilt.sh index 4ddd298..e9e449a 100755 --- a/desktop/packaging/arch/package-prebuilt.sh +++ b/desktop/packaging/arch/package-prebuilt.sh @@ -29,8 +29,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" SRC_TAURI="$REPO_ROOT/desktop/src-tauri" -BINARY="$SRC_TAURI/target/release/thoughtsync" -OUT_DIR="${1:-$SRC_TAURI/target/release/bundle/arch}" +BINARY="$REPO_ROOT/target/release/thoughtsync" +OUT_DIR="${1:-$REPO_ROOT/target/release/bundle/arch}" PKGNAME="thoughtsync" # The name this package used to ship under. pacman needs both to retire it: without diff --git a/desktop/packaging/deb/verify.sh b/desktop/packaging/deb/verify.sh index c8650ea..9c2fe0a 100755 --- a/desktop/packaging/deb/verify.sh +++ b/desktop/packaging/deb/verify.sh @@ -33,7 +33,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" -DEB_DIR="$REPO_ROOT/desktop/src-tauri/target/release/bundle/deb" +DEB_DIR="$REPO_ROOT/target/release/bundle/deb" shopt -s nullglob DEBS=("$DEB_DIR"/*.deb) diff --git a/desktop/packaging/publish-release.sh b/desktop/packaging/publish-release.sh index ebcf027..06638a6 100755 --- a/desktop/packaging/publish-release.sh +++ b/desktop/packaging/publish-release.sh @@ -14,10 +14,10 @@ # for a tag that already exists. # # The build + de-bundle steps run first; this consumes their output: -# desktop/src-tauri/target/release/bundle/appimage/*.AppImage (de-bundled) -# desktop/src-tauri/target/release/bundle/deb/*.deb -# desktop/src-tauri/target/release/bundle/arch/*.pkg.tar.* (prebuilt pacman) -# desktop/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe +# target/release/bundle/appimage/*.AppImage (de-bundled) +# target/release/bundle/deb/*.deb +# target/release/bundle/arch/*.pkg.tar.* (prebuilt pacman) +# target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe # # Instance-agnostic: server + repo come from the runner's github.* context # (Forgejo populates them for compatibility), so nothing is hardcoded to one host. @@ -44,9 +44,9 @@ AUTH=(-H "Authorization: token $GITHUB_TOKEN") SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -BUNDLE_ROOT="$REPO_ROOT/desktop/src-tauri/target/release/bundle" +BUNDLE_ROOT="$REPO_ROOT/target/release/bundle" # Cross-compiled Windows output lands under the target triple, not the host root. -WIN_BUNDLE_ROOT="$REPO_ROOT/desktop/src-tauri/target/x86_64-pc-windows-msvc/release/bundle" +WIN_BUNDLE_ROOT="$REPO_ROOT/target/x86_64-pc-windows-msvc/release/bundle" # --- collect the assets to upload ------------------------------------------- shopt -s nullglob diff --git a/desktop/src-tauri/.gitignore b/desktop/src-tauri/.gitignore index 732d389..398b54e 100644 --- a/desktop/src-tauri/.gitignore +++ b/desktop/src-tauri/.gitignore @@ -1,5 +1,2 @@ -# Generated by Cargo -/target/ - # Generated by Tauri /gen/schemas diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 538748a..6781e93 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -9,11 +9,15 @@ edition = "2021" # src/main.rs. autobins = false -# App logic lives in the library (Tauri v2 pattern: a single `run()` entry point -# reusable across desktop and any future mobile target); main.rs is a thin shim. +# App logic lives in the library (Tauri v2 pattern: a single `run()` entry point); +# main.rs is a thin shim. +# +# rlib only. The staticlib/cdylib types existed for Tauri mobile, which Android no +# longer uses — it is a native Kotlin client over `thoughtsync-core` instead +# (Scribe note 2730), and the .so it loads is built from that crate, not this one. [lib] name = "thoughtsync_desktop_lib" -crate-type = ["staticlib", "cdylib", "rlib"] +crate-type = ["rlib"] # The shipped command is `thoughtsync` on every install channel, and the binary's # own name is what the desktop actually keys on: GTK derives the window's WM_CLASS @@ -32,58 +36,19 @@ path = "src/main.rs" tauri-build = { version = "2", features = [] } [dependencies] +# The framework-free client core: local SQLite store + sync engine. Shared with the +# Android client, which binds the same crate through uniffi (Scribe note 2730). +thoughtsync-core = { path = "../../core" } + tauri = { version = "2", features = [] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -# Local-first store (M10.4): bundled = compile SQLite in, so there's no system -# libsqlite dependency to vary across the AppImage / native / Windows builds. -rusqlite = { version = "0.32", features = ["bundled"] } -uuid = { version = "1", features = ["v4"] } -# RFC3339 timestamps for created_at/updated_at/remind_at (Date.parse-able on the JS side). -chrono = { version = "0.4", default-features = false, features = ["clock"] } +serde = { workspace = true } +serde_json = { workspace = true } +log = { workspace = true } # Startup + operation logging to stdout AND a persistent file, so portability -# issues are diagnosable from any environment. `log` is the facade the code uses. +# issues are diagnosable from any environment. tauri-plugin-log = "2" -log = "0.4" # In-app updates (M10.9). Signature verification is minisign; the public half lives -# in tauri.conf.json and the private half only ever as a CI secret. +# in tauri.conf.json and the private half only ever as a CI secret. Desktop only — +# the plugin declares android support level "none", which is why the Android client +# gets a server-served update path instead (Scribe note 2725). tauri-plugin-updater = "2" -# HTTP for the opt-in server handshake (M10.6) and, next, the sync engine (M10.7). -# -# native-tls, NOT rustls, deliberately: on x86_64-pc-windows-msvc native-tls -# resolves to `schannel` — pure-Rust bindings to the OS TLS stack — so nothing C or -# assembly has to cross-compile on the Windows lane, which is the fragile one (it -# builds on Linux via cargo-xwin, and a C dependency there is what broke it before). -# rustls would instead pull in ring/aws-lc-rs and their assembler. On Linux -# native-tls uses OpenSSL, whose headers (libssl-dev) ci-tauri already ships. -# default-features off drops http2/charset we don't need for a JSON API. -reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] } -# Verifying downloaded attachment bytes against the sha256 the server advertised. -# Pure Rust (no C/asm beyond optional cpufeatures), so it costs the Windows -# cross-compile lane nothing — see ci-requirements.md on why that matters here. -sha2 = "0.10" - -# Android has no system OpenSSL to link against, and `native-tls` resolves to -# OpenSSL there — unlike Windows, where it lands on schannel and costs nothing. -# Without this the build dies at `openssl-sys`: "Could not find directory of -# OpenSSL installation". -# -# `vendored` compiles OpenSSL from source with the NDK toolchain (hence perl + make -# in ci-tauri-android). The alternative was rustls on Android only, which builds -# faster — but rustls ships its own root store, so the phone would trust a -# DIFFERENT set of certificates than the desktop does. A self-hosted server behind -# a private or enterprise CA would then work on one surface and fail on another, -# and "the surfaces behave the same" is worth more here than build minutes. -# -# Declared as a direct dependency purely to turn the feature on: cargo's feature -# unification applies it to the copy `native-tls` pulls in transitively. -[target.'cfg(target_os = "android")'.dependencies] -openssl-sys = { version = "0.9", features = ["vendored"] } - -# Tauri's default release profile: smaller, faster shipped binaries. -[profile.release] -codegen-units = 1 -lto = true -opt-level = "s" -panic = "abort" -strip = true diff --git a/desktop/src-tauri/gen/android/.editorconfig b/desktop/src-tauri/gen/android/.editorconfig deleted file mode 100644 index ebe51d3..0000000 --- a/desktop/src-tauri/gen/android/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# top-most EditorConfig file -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = false -insert_final_newline = false \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/.gitignore b/desktop/src-tauri/gen/android/.gitignore deleted file mode 100644 index 1c636c3..0000000 --- a/desktop/src-tauri/gen/android/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -build -/captures -.externalNativeBuild -.cxx -local.properties -key.properties -keystore.properties - -/.tauri -/tauri.settings.gradle \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/.gitignore b/desktop/src-tauri/gen/android/app/.gitignore deleted file mode 100644 index 6c4d56b..0000000 --- a/desktop/src-tauri/gen/android/app/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/src/main/**/generated -/src/main/jniLibs/**/*.so -/src/main/assets/tauri.conf.json -/tauri.build.gradle.kts -/proguard-tauri.pro -/tauri.properties \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/build.gradle.kts b/desktop/src-tauri/gen/android/app/build.gradle.kts deleted file mode 100644 index beae1a0..0000000 --- a/desktop/src-tauri/gen/android/app/build.gradle.kts +++ /dev/null @@ -1,71 +0,0 @@ -import java.util.Properties - -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") - id("rust") -} - -val tauriProperties = Properties().apply { - val propFile = file("tauri.properties") - if (propFile.exists()) { - propFile.inputStream().use { load(it) } - } -} - -android { - compileSdk = 36 - namespace = "com.fabledsword.thoughtsync" - defaultConfig { - manifestPlaceholders["usesCleartextTraffic"] = "false" - applicationId = "com.fabledsword.thoughtsync" - minSdk = 24 - targetSdk = 36 - versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() - versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") - } - buildTypes { - getByName("debug") { - manifestPlaceholders["usesCleartextTraffic"] = "true" - isDebuggable = true - isJniDebuggable = true - isMinifyEnabled = false - packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") - jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") - jniLibs.keepDebugSymbols.add("*/x86/*.so") - jniLibs.keepDebugSymbols.add("*/x86_64/*.so") - } - } - getByName("release") { - isMinifyEnabled = true - proguardFiles( - *fileTree(".") { include("**/*.pro") } - .plus(getDefaultProguardFile("proguard-android-optimize.txt")) - .toList().toTypedArray() - ) - } - } - kotlinOptions { - jvmTarget = "1.8" - } - buildFeatures { - buildConfig = true - } -} - -rust { - rootDirRel = "../../../" -} - -dependencies { - implementation("androidx.webkit:webkit:1.14.0") - implementation("androidx.appcompat:appcompat:1.7.1") - implementation("androidx.activity:activity-ktx:1.10.1") - implementation("com.google.android.material:material:1.12.0") - implementation("androidx.lifecycle:lifecycle-process:2.10.0") - testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.4") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") -} - -apply(from = "tauri.build.gradle.kts") \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/proguard-rules.pro b/desktop/src-tauri/gen/android/app/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/desktop/src-tauri/gen/android/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/desktop/src-tauri/gen/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index f834da9..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/desktop/src-tauri/gen/android/app/src/main/java/com/fabledsword/thoughtsync/MainActivity.kt b/desktop/src-tauri/gen/android/app/src/main/java/com/fabledsword/thoughtsync/MainActivity.kt deleted file mode 100644 index abd4eb3..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/java/com/fabledsword/thoughtsync/MainActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.fabledsword.thoughtsync - -import android.os.Bundle -import androidx.activity.enableEdgeToEdge - -class MainActivity : TauriActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - enableEdgeToEdge() - super.onCreate(savedInstanceState) - } -} diff --git a/desktop/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/desktop/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml b/desktop/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/desktop/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml b/desktop/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 4fc2444..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 28f1aa1..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 85d0c88..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 28f1aa1..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 73e48db..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index 13dd214..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 73e48db..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 1d98044..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index a888b33..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 1d98044..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 0818324..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index a2a838e..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 0818324..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index b18bceb..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 3f8a57f..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index b18bceb..0000000 Binary files a/desktop/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/desktop/src-tauri/gen/android/app/src/main/res/values-night/themes.xml b/desktop/src-tauri/gen/android/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 9f62421..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/desktop/src-tauri/gen/android/app/src/main/res/values/colors.xml b/desktop/src-tauri/gen/android/app/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/src/main/res/values/strings.xml b/desktop/src-tauri/gen/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 3bc5ea0..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - "ThoughtSync" - "ThoughtSync" - \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/app/src/main/res/values/themes.xml b/desktop/src-tauri/gen/android/app/src/main/res/values/themes.xml deleted file mode 100644 index 9f62421..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/desktop/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml b/desktop/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml deleted file mode 100644 index 782d63b..0000000 --- a/desktop/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/desktop/src-tauri/gen/android/build.gradle.kts b/desktop/src-tauri/gen/android/build.gradle.kts deleted file mode 100644 index 607240b..0000000 --- a/desktop/src-tauri/gen/android/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.android.tools.build:gradle:8.11.0") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -tasks.register("clean").configure { - delete("build") -} - diff --git a/desktop/src-tauri/gen/android/buildSrc/build.gradle.kts b/desktop/src-tauri/gen/android/buildSrc/build.gradle.kts deleted file mode 100644 index 5c55bba..0000000 --- a/desktop/src-tauri/gen/android/buildSrc/build.gradle.kts +++ /dev/null @@ -1,23 +0,0 @@ -plugins { - `kotlin-dsl` -} - -gradlePlugin { - plugins { - create("pluginsForCoolKids") { - id = "rust" - implementationClass = "RustPlugin" - } - } -} - -repositories { - google() - mavenCentral() -} - -dependencies { - compileOnly(gradleApi()) - implementation("com.android.tools.build:gradle:8.11.0") -} - diff --git a/desktop/src-tauri/gen/android/buildSrc/src/main/java/com/fabledsword/thoughtsync/kotlin/BuildTask.kt b/desktop/src-tauri/gen/android/buildSrc/src/main/java/com/fabledsword/thoughtsync/kotlin/BuildTask.kt deleted file mode 100644 index a1b43c0..0000000 --- a/desktop/src-tauri/gen/android/buildSrc/src/main/java/com/fabledsword/thoughtsync/kotlin/BuildTask.kt +++ /dev/null @@ -1,68 +0,0 @@ -import java.io.File -import org.apache.tools.ant.taskdefs.condition.Os -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.logging.LogLevel -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.TaskAction - -open class BuildTask : DefaultTask() { - @Input - var rootDirRel: String? = null - @Input - var target: String? = null - @Input - var release: Boolean? = null - - @TaskAction - fun assemble() { - val executable = """cargo"""; - try { - runTauriCli(executable) - } catch (e: Exception) { - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - // Try different Windows-specific extensions - val fallbacks = listOf( - "$executable.exe", - "$executable.cmd", - "$executable.bat", - ) - - var lastException: Exception = e - for (fallback in fallbacks) { - try { - runTauriCli(fallback) - return - } catch (fallbackException: Exception) { - lastException = fallbackException - } - } - throw lastException - } else { - throw e; - } - } - } - - fun runTauriCli(executable: String) { - val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") - val target = target ?: throw GradleException("target cannot be null") - val release = release ?: throw GradleException("release cannot be null") - val args = listOf("tauri", "android", "android-studio-script"); - - project.exec { - workingDir(File(project.projectDir, rootDirRel)) - executable(executable) - args(args) - if (project.logger.isEnabled(LogLevel.DEBUG)) { - args("-vv") - } else if (project.logger.isEnabled(LogLevel.INFO)) { - args("-v") - } - if (release) { - args("--release") - } - args(listOf("--target", target)) - }.assertNormalExitValue() - } -} \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/buildSrc/src/main/java/com/fabledsword/thoughtsync/kotlin/RustPlugin.kt b/desktop/src-tauri/gen/android/buildSrc/src/main/java/com/fabledsword/thoughtsync/kotlin/RustPlugin.kt deleted file mode 100644 index 4aa7fca..0000000 --- a/desktop/src-tauri/gen/android/buildSrc/src/main/java/com/fabledsword/thoughtsync/kotlin/RustPlugin.kt +++ /dev/null @@ -1,85 +0,0 @@ -import com.android.build.api.dsl.ApplicationExtension -import org.gradle.api.DefaultTask -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.get - -const val TASK_GROUP = "rust" - -open class Config { - lateinit var rootDirRel: String -} - -open class RustPlugin : Plugin { - private lateinit var config: Config - - override fun apply(project: Project) = with(project) { - config = extensions.create("rust", Config::class.java) - - val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64"); - val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList - - val defaultArchList = listOf("arm64", "arm", "x86", "x86_64"); - val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList - - val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64") - - extensions.configure { - @Suppress("UnstableApiUsage") - flavorDimensions.add("abi") - productFlavors { - create("universal") { - dimension = "abi" - ndk { - abiFilters += abiList - } - } - defaultArchList.forEachIndexed { index, arch -> - create(arch) { - dimension = "abi" - ndk { - abiFilters.add(defaultAbiList[index]) - } - } - } - } - } - - afterEvaluate { - for (profile in listOf("debug", "release")) { - val profileCapitalized = profile.replaceFirstChar { it.uppercase() } - val buildTask = tasks.maybeCreate( - "rustBuildUniversal$profileCapitalized", - DefaultTask::class.java - ).apply { - group = TASK_GROUP - description = "Build dynamic library in $profile mode for all targets" - } - - tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask) - - for (targetPair in targetsList.withIndex()) { - val targetName = targetPair.value - val targetArch = archList[targetPair.index] - val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() } - val targetBuildTask = project.tasks.maybeCreate( - "rustBuild$targetArchCapitalized$profileCapitalized", - BuildTask::class.java - ).apply { - group = TASK_GROUP - description = "Build dynamic library in $profile mode for $targetArch" - rootDirRel = config.rootDirRel - target = targetName - release = profile == "release" - } - - buildTask.dependsOn(targetBuildTask) - tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn( - targetBuildTask - ) - } - } - } - } -} \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/gradle.properties b/desktop/src-tauri/gen/android/gradle.properties deleted file mode 100644 index 2a7ec69..0000000 --- a/desktop/src-tauri/gen/android/gradle.properties +++ /dev/null @@ -1,24 +0,0 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official -# Enables namespacing of each library's R class so that its R class includes only the -# resources declared in the library itself and none from the library's dependencies, -# thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true -android.nonFinalResIds=false \ No newline at end of file diff --git a/desktop/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar b/desktop/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c..0000000 Binary files a/desktop/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/desktop/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties b/desktop/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index c5f9a53..0000000 --- a/desktop/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Tue May 10 19:22:52 CST 2022 -distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME diff --git a/desktop/src-tauri/gen/android/gradlew b/desktop/src-tauri/gen/android/gradlew deleted file mode 100755 index 4f906e0..0000000 --- a/desktop/src-tauri/gen/android/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/desktop/src-tauri/gen/android/gradlew.bat b/desktop/src-tauri/gen/android/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/desktop/src-tauri/gen/android/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/desktop/src-tauri/gen/android/settings.gradle b/desktop/src-tauri/gen/android/settings.gradle deleted file mode 100644 index 3939116..0000000 --- a/desktop/src-tauri/gen/android/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -include ':app' - -apply from: 'tauri.settings.gradle' diff --git a/desktop/src-tauri/src/local/commands.rs b/desktop/src-tauri/src/commands/local.rs similarity index 98% rename from desktop/src-tauri/src/local/commands.rs rename to desktop/src-tauri/src/commands/local.rs index 312185b..f294545 100644 --- a/desktop/src-tauri/src/local/commands.rs +++ b/desktop/src-tauri/src/commands/local.rs @@ -6,11 +6,11 @@ use serde_json::Value; use tauri::State; -use crate::local::models::*; -use crate::local::retention; -use crate::local::store; -use crate::local::Db; -use crate::sync::state; +use thoughtsync_core::local::models::*; +use thoughtsync_core::local::retention; +use thoughtsync_core::local::store; +use thoughtsync_core::local::Db; +use thoughtsync_core::sync::state; // A macro would hide the (very regular) locking; kept explicit so each command reads // as an obvious lock -> delegate -> stringify. diff --git a/desktop/src-tauri/src/commands/mod.rs b/desktop/src-tauri/src/commands/mod.rs new file mode 100644 index 0000000..364bd4a --- /dev/null +++ b/desktop/src-tauri/src/commands/mod.rs @@ -0,0 +1,10 @@ +//! The Tauri command surface — the desktop's adapter onto `thoughtsync-core`. +//! +//! This is the whole of what couples the store and sync engine to Tauri, and keeping +//! it in one place is deliberate: the Android client writes its own adapter (uniffi) +//! against the same core, so anything that leaks framework concerns back into the +//! core makes that second adapter harder. `frontend/src/adapters/local.ts` calls in +//! here over `invoke`. + +pub mod local; +pub mod sync; diff --git a/desktop/src-tauri/src/sync/commands.rs b/desktop/src-tauri/src/commands/sync.rs similarity index 96% rename from desktop/src-tauri/src/sync/commands.rs rename to desktop/src-tauri/src/commands/sync.rs index 9999103..93195bd 100644 --- a/desktop/src-tauri/src/sync/commands.rs +++ b/desktop/src-tauri/src/commands/sync.rs @@ -6,13 +6,13 @@ use serde::{Deserialize, Serialize}; use tauri::State; -use crate::local::Db; -use crate::sync::blobs::BlobStore; -use crate::sync::client::{self, Identity, ProbeResult}; -use crate::sync::compat::Compatibility; -use crate::sync::engine; -use crate::sync::push; -use crate::sync::state; +use thoughtsync_core::local::Db; +use thoughtsync_core::sync::blobs::BlobStore; +use thoughtsync_core::sync::client::{self, Identity, ProbeResult}; +use thoughtsync_core::sync::compat::Compatibility; +use thoughtsync_core::sync::engine; +use thoughtsync_core::sync::push; +use thoughtsync_core::sync::state; /// Ask a server who it is, without committing to anything. The UI calls this as the /// user finishes typing an address, so they see what answered before handing over diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 2322010..77c075b 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -1,20 +1,24 @@ //! ThoughtSync desktop (Tauri v2). //! -//! The window loads the shared Vue 3 frontend (`../../frontend`). The Rust core will -//! own the on-device SQLite store (M10.4) and the opt-in sync engine (M10.7), which -//! the frontend reaches through the `frontend/src/adapters/` seam (M10.3) over Tauri -//! `invoke`. Today it exposes desktop integration (menu-entry install for the Linux -//! AppImage) and boots the UI. +//! The window loads the shared Vue 3 frontend (`../../frontend`), which reaches the +//! store and sync engine through the `frontend/src/adapters/` seam (M10.3) over Tauri +//! `invoke`. +//! +//! This crate is the DESKTOP WRAPPER, not the core. The on-device SQLite store and +//! the sync engine live in `thoughtsync-core`, shared with the Android client; what +//! remains here is the Tauri command surface (`commands`), desktop integration +//! (menu-entry install for the Linux AppImage), the in-app updater, and boot. +mod commands; mod integration; -mod local; -// `pub` (unlike the modules above) because parts of it have no in-crate caller yet — -// the engine that will consume them is M10.7b/c, and a private module's unreachable -// items read as dead code. -pub mod sync; mod update; -#[cfg_attr(mobile, tauri::mobile_entry_point)] +// The store and the sync engine live in the shared `thoughtsync-core` crate, which +// the Android client binds through uniffi (Scribe note 2730). Aliased to their old +// names so every call site below reads exactly as it did when they were modules of +// this crate — the extraction changed where they live, not what they are. +use thoughtsync_core::{local, sync}; + pub fn run() { use tauri_plugin_log::{Target, TargetKind}; @@ -93,48 +97,48 @@ pub fn run() { integration::integration_status, integration::integrate_desktop, integration::unintegrate_desktop, - local::commands::config_get, - local::commands::auth_me, - local::commands::notes_list, - local::commands::notes_get, - local::commands::notes_create, - local::commands::notes_create_titled, - local::commands::notes_update, - local::commands::notes_complete_reminder, - local::commands::notes_snooze_reminder, - local::commands::notes_set_labels, - local::commands::notes_add_item, - local::commands::notes_update_item, - local::commands::notes_delete_item, - local::commands::notes_delete_attachment, - local::commands::notes_delete_preview, - local::commands::notes_reorder, - local::commands::notes_trash, - local::commands::notes_restore, - local::commands::notes_delete_forever, - local::commands::notes_revisions, - local::commands::notes_restore_revision, - local::commands::notes_reminders, - local::commands::notes_titles, - local::commands::notes_search, - local::commands::notes_backlinks, - local::commands::notes_link_search, - local::commands::labels_list, - local::commands::labels_create, - local::commands::labels_rename, - local::commands::labels_set_color, - local::commands::labels_remove, - local::commands::labels_merge, - local::commands::saved_filters_list, - local::commands::saved_filters_create, - local::commands::saved_filters_remove, - local::commands::saved_filters_rename, - sync::commands::sync_probe, - sync::commands::sync_link, - sync::commands::sync_unlink, - sync::commands::sync_status, - sync::commands::sync_now, - sync::commands::sync_has_pending, + commands::local::config_get, + commands::local::auth_me, + commands::local::notes_list, + commands::local::notes_get, + commands::local::notes_create, + commands::local::notes_create_titled, + commands::local::notes_update, + commands::local::notes_complete_reminder, + commands::local::notes_snooze_reminder, + commands::local::notes_set_labels, + commands::local::notes_add_item, + commands::local::notes_update_item, + commands::local::notes_delete_item, + commands::local::notes_delete_attachment, + commands::local::notes_delete_preview, + commands::local::notes_reorder, + commands::local::notes_trash, + commands::local::notes_restore, + commands::local::notes_delete_forever, + commands::local::notes_revisions, + commands::local::notes_restore_revision, + commands::local::notes_reminders, + commands::local::notes_titles, + commands::local::notes_search, + commands::local::notes_backlinks, + commands::local::notes_link_search, + commands::local::labels_list, + commands::local::labels_create, + commands::local::labels_rename, + commands::local::labels_set_color, + commands::local::labels_remove, + commands::local::labels_merge, + commands::local::saved_filters_list, + commands::local::saved_filters_create, + commands::local::saved_filters_remove, + commands::local::saved_filters_rename, + commands::sync::sync_probe, + commands::sync::sync_link, + commands::sync::sync_unlink, + commands::sync::sync_status, + commands::sync::sync_now, + commands::sync::sync_has_pending, update::update_channel_get, update::update_channel_set, update::update_check, diff --git a/desktop/src-tauri/src/update.rs b/desktop/src-tauri/src/update.rs index 1537863..86d589b 100644 --- a/desktop/src-tauri/src/update.rs +++ b/desktop/src-tauri/src/update.rs @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize}; use tauri::State; use tauri_plugin_updater::UpdaterExt; -use crate::local::{store, Db}; +use thoughtsync_core::local::{store, Db}; /// Where the manifests live. Fixed tags, so these URLs are permanent. const FEED_BASE: &str = "https://git.fabledsword.com/bvandeusen/thoughtsync/releases/download"; @@ -308,9 +308,9 @@ fn describe_check_error(detail: &str) -> String { #[cfg(test)] mod tests { use super::*; - use crate::local::schema; use rusqlite::Connection; use std::sync::Mutex; + use thoughtsync_core::local::schema; fn db() -> Db { let conn = Connection::open_in_memory().expect("in-memory db"); diff --git a/docs/android-dev.md b/docs/android-dev.md deleted file mode 100644 index 90212f5..0000000 --- a/docs/android-dev.md +++ /dev/null @@ -1,117 +0,0 @@ -# Running the Android client locally - -The Android app is the same Vue frontend and Rust core as the desktop, built by -Tauri v2 mobile. This sheet is the local setup; CI's copy of it is the -`ci-tauri-android` image (see `ci-requirements.md`). - -**Open this directory in Android Studio:** - -``` -desktop/src-tauri/gen/android -``` - -Two things must be true first, and both fail confusingly if they aren't. - -## 1. Gradle must run on JDK 17 - -Android Studio defaults to its own bundled JDK, which is currently 25. That -produces: - -> Incompatible Gradle JVM version — The project's Gradle version 8.14.3 is -> incompatible with the Gradle JVM version 25 currently selected to run Gradle -> build. - -Fix: `Settings → Build, Execution, Deployment → Build Tools → Gradle → Gradle -JDK` → **17**. If none is listed, `Download JDK…` → version 17, vendor Eclipse -Temurin. - -Prefer 17 explicitly over Android Studio's "apply a compatible JDK" quick-fix, -which will likely pick 21. 17 is what `ci-tauri-android` pins, and matching it -means a local build and a CI build resolve identically. - -**Why 8.14.3 and not Gradle 9:** `cargo tauri android init` generated the wrapper -pin, and Gradle 9 removed `project.exec`, which the generated -`buildSrc/.../BuildTask.kt` still uses. The wrapper, the AGP pin and that file are -all tracked in this repo, so the bump is ours to make whenever it is worth doing — -it is scaffolding we inherited, not a requirement of Tauri. Tauri's own Android -layer targets `compileSdk 36` and registers back handling through -`OnBackPressedDispatcher`, so the library is current; only the template trails. - -## 2. The Gradle glue has to be generated once - -`settings.gradle` does `apply from: 'tauri.settings.gradle'`, and that file — with -`app/tauri.build.gradle.kts`, `app/tauri.properties`, -`app/src/main/assets/tauri.conf.json` and the compiled `.so` files — is generated -per build and gitignored. A fresh clone does not have them, and settings -evaluation happens before any Gradle task, so the sync fails before anything can -generate them. - -From `desktop/src-tauri`: - -```sh -cargo tauri android build --debug --target x86_64 -``` - -After that, Android Studio syncs cleanly. - -## Prerequisites - -Gradle shells out to `cargo tauri android android-studio-script` on every build, -so the Rust toolchain is required on the machine running Android Studio — the CI -image cannot stand in for it. - -```sh -rustup toolchain install 1.97.1 -rustup target add aarch64-linux-android x86_64-linux-android -cargo install tauri-cli --version 2.11.4 --locked -``` - -Via the SDK Manager, matching `CI-tauri-android/versions.env`: - -| | version | why it matters | -|---|---|---| -| Platform | API 36 | `compileSdk` / `targetSdk` | -| Build-Tools | 35.0.0 | AGP 8.11's default | -| **NDK** | 28.2.13676358 | the Rust core cannot cross-compile without it | -| Platform-Tools | latest | `adb` | - -```sh -export ANDROID_HOME=$HOME/Android/Sdk -export NDK_HOME=$ANDROID_HOME/ndk/28.2.13676358 -``` - -Node is needed too — `beforeBuildCommand` runs `npm ci && npm run build`, because -`generate_context!` compiles the frontend into the binary. - -## Emulator - -Create an AVD with an **x86_64** system image, API 24 or newer (`minSdk = 24`). -CI builds arm64-v8a and x86_64, so either runs, but x86_64 executes natively on a -desktop instead of being translated. - -## The dev loop - -Android Studio's Run button rebuilds the whole Rust core each time. For -iteration, with an emulator already running: - -```sh -cd desktop/src-tauri -cargo tauri android dev -``` - -That starts Vite, wires `adb reverse` so the emulator reaches -`localhost:5173`, and hot-reloads frontend changes without a rebuild. Android -Studio earns its keep for the debugger, logcat and layout inspection. - -## Known gaps, so they are not mistaken for bugs - -- **Safe areas / viewport** are not handled yet (task 2706) — content may sit - under a notch or the gesture bar. Deliberately left until a device could - verify it. -- **`android:enableOnBackInvokedCallback`** is absent from the manifest, so - predictive-back animations will not engage on Android 13+. Tauri registers the - dispatcher correctly; only the opt-in flag is missing. -- **`android:usesCleartextTraffic`** is templated in app-wide. A - `networkSecurityConfig` resource is the better shape — see Scribe note 2437, - which hit this on Minstrel. -- Debug builds are large and slow to start. Do not read either as the product.