Skip to content
logseqPublic

About

Logseq mobile v2

Resources

Stars

7 stars

Watchers

1 watching

Forks

Repository files navigation

Logseq

Logseq is a native SwiftUI app for iOS and a Kotlin/Jetpack Compose app for Android. Both clients share an OCaml application model and core, backed by the OCaml DataScript library and the lui reactive UI layer.

iOS and Android share the app version in version.xcconfig. Android builds, the iOS build scripts, and the TestFlight lane embed the mobile repository's current Git commit as the settings revision.

PDF assets open full-screen in narrow windows and beside the main editor in wide windows, with a control to switch modes. The viewer uses PDFKit on iOS/iPadOS and AndroidX PDF Viewer on Android. Highlights are stored as Logseq Pdf-annotation blocks in the graph and use the normal offline persistence and sync queue; the original PDF stays unchanged.

Shared core sources live in shared/src/logseq/core, with executable entry points in shared/src/logseq/entry. The shared/native directory contains Dune configuration and native platform bridges; generated OCaml stays under _build. Core tests live in shared/test/logseq, one alcotest suite per core module. The core test gate also builds the seed and live-sync executables.

Natural-language dates use chrono-node's English parser in an on-demand QuickJS runtime shared by iOS and Android. Calculator blocks use BigNumber.js with the master calculator's operators, scientific functions, variables, format directives and base conversion; their independent runtime is also created only on demand. The first core build downloads checksum-pinned sources listed in shared/native/nlp/dependencies.json; Python 3 is required. Archives are cached in $XDG_CACHE_HOME/logseq/native-dependencies (defaulting to ~/.cache) and generated sources stay under _build. Core startup and canonical date input do not initialize the JavaScript engine.

Repository layout

  • apple/: Swift package, app, extensions, Xcode workspace, and Apple tests.
  • android/: Kotlin/Jetpack Compose Android app and its platform integration.
  • shared/src/: OCaml application and core sources.
  • shared/test/: OCaml test suites.
  • shared/native/: Dune configuration and native bridges.
  • tests/e2e/: Maestro flows and fixtures.
  • scripts/: build and validation commands, run from the repository root.
  • docs/: design and development documentation.
  • duniverse/: fetched OCaml dependencies; ignored by Git.

iOS

Open apple/Project.xcworkspace and run the Logseq App scheme in Xcode.

The Apple backend comes from the LUIAppleBackendStatic product of the LUI Swift package, pinned by Git revision in apple/Package.swift and apple/Package.resolved. Use Swift 6.2 or later. Backend changes belong in the LUI repository. To test a local backend checkout, run swift package --package-path apple edit lui --path ../lui before building. Use swift package --package-path apple unedit lui to return to the pinned version.

The first iOS build creates a deployment-targeted OCaml 5.5 toolchain under _build/apple-toolchains. Native dependencies and core objects are keyed by compiler, target, and source fingerprints, so later builds reuse them. Set LOGSEQ_APPLE_TOOLCHAIN_ROOT to share the toolchain cache, or provide LOGSEQ_IOS_TOOLCHAIN_PREFIX to use an existing compatible compiler directly.

scripts/install-mobile-ios-device.sh uses signing and device settings from .logseq-ios-device.env. To replace an existing Logseq installation, use LOGSEQ_IOS_BUNDLE_ID=com.logseq.logseq and explicitly set LOGSEQ_IOS_ALLOW_PRODUCTION_REPLACE=1, with a profile and signing identity from the same Apple team as the installed app.

Device builds use the optimized Release configuration by default. Set LOGSEQ_IOS_BUILD_CONFIGURATION=debug when you need an unoptimized build for debugging; use Release builds when comparing startup and scrolling performance.

Device builds embed both the widget and share extensions. Matching development profiles are selected automatically; set LOGSEQ_IOS_WIDGET_PROFILE or LOGSEQ_IOS_SHARE_PROFILE to select a profile explicitly.

Android

Android 9 (API 28) or later is required.

Android is a standard Gradle project:

cd android
./gradlew :app:assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk

Release builds use :app:assembleRelease / :app:bundleRelease. The e2e profile APK used by scripts/test-android-e2e.sh is :app:assembleProfile (app/build/outputs/apk/profile/app-profile.apk). JVM unit tests run with ./gradlew test.

Instrumented tests default to the Debug build. To test an optimized Release, build its matching test APK in the same invocation:

./gradlew :app:assembleRelease :app:assembleReleaseAndroidTest -PlogseqTestBuildType=release

Sign the application and test APK with the same certificate before installing them. The Release test build applies the application's R8 mapping to test code.

The OCaml core shared library is built separately by scripts/build-android-native.sh (per ABI), which installs liblogseq_core.so into android/app/src/main/jniLibs/<abi>; the Gradle buildAndroidNativeCore task invokes it automatically (set -PlogseqRequireNativeCore=false to compile Kotlin without it).

The Android host includes Capture and Journal app shortcuts, Capture and Today’s Journal home-screen widgets, inbound sharing, deep links, native authentication, media services, and the OCaml core JNI library.

iPhone shortcuts and widgets

Long-press the app icon for Voice and Quick Add. The Shortcuts app exposes Quick Add, Record Audio, Today's Journal, and Capture to Journal (text input). The Quick Capture home-screen widget includes recording and capture buttons; Today's Journal opens the journal. On iOS 18 and later, Quick Add and Record Audio are also available in Control Center and on the Lock Screen. These entry points open the existing composer and recorder, including when the app needs to launch first.

Both iOS build scripts include App Intents metadata and the widget extension. Device builds select a compatible development profile for com.logseq.logseq.shortcuts; set LOGSEQ_IOS_WIDGET_PROFILE in .logseq-ios-device.env to specify one. The extension and app must be signed by the same team, and their profiles must include the target device.

Testing

Do not run swift test in this repository because the XCTest bridge hangs in the current environment. Use the supported gates instead: Run the model suite without that bridge using python3 scripts/test-swift-model-suite.py. It builds LogseqModelTests and launches Swift Testing directly. Run the native UI suite in a simulator app host with python3 scripts/test-swift-ui-suite.py after building the simulator core. This also invokes Swift Testing directly. For iPhone Duo, build with Xcode 27.1 or later. Custom navigation controls follow the system toolbar axis, including the vertical bar on the outer display. Collapsed photo, audio recording, search and capture actions stay in a shared trailing rail in that order, with matching SF Symbols. The rail stays outside page transitions so navigation cannot relocate its buttons; the expanded composer stays above the keyboard. Build with LOGSEQ_IOS_ENABLE_TESTING=1 scripts/build-mobile-ios-simulator.sh, then run LOGSEQ_RUNTIME_CHECK_TIMEOUT=65 python3 apple/Tests/UI/run-simulator-runtime-check.py apple/Tests/UI/duo-toolbar-layout.swift DuoToolbarLayoutCheck. Select the device with LOGSEQ_IOS_SIMULATOR=<UDID> when multiple simulators are booted. Change poses in Device Hub during the 40-second check; it verifies visible, separate sync/calendar and capture targets and saves a screenshot for each stable layout. Set SIMCTL_CHILD_LOGSEQ_DUO_REQUIRE_POSE_CHANGE=1 to require both toolbar axes, and LOGSEQ_RUNTIME_CHECK_ARTIFACTS=/path/to/output to retain the screenshots. Set SIMCTL_CHILD_LOGSEQ_DUO_CHECK_INTERACTIONS=1 to also verify capture/search focus, keyboard avoidance and unsent draft retention on the vertical layout. Capture controls, the native recording sheet and file download notices share all 25 supported languages in apple/Sources/Logseq/Resources/CaptureLocalizations.json. After editing it, run python3 scripts/generate-capture-localizations.py to update the shared dictionary; --check verifies language coverage and generated output. Run apple/Tests/UI/duo-search-dismiss-stability.swift with the runtime runner to check journal position on every display frame while dismissing native search. duo-page-navigation-chrome.swift checks the native glass rail bounds during repeated nested page pushes and native back transitions. duo-appearance-responsiveness.swift switches appearance, rotates the display and scrolls the journal, checking that input remains responsive and idle CPU settles. pdf-download-feedback.swift covers an uncached annotation PDF, progress through cache persistence, completion, target-page restoration, cache hits, errors and cancellation. pdf-annotation-page-link.swift also verifies that opening a PDF hides the sidebar button and closing it restores the button. Run LOGSEQ_IOS_CONFIGURATION=release python3 scripts/test-ios-native-menus.py after a Release simulator build to exercise native composer menus with Maestro. It checks every attachment presenter, recording import, repeated cancellation, all task statuses, and the search close button without a server or credentials. Add --hit-testing to check independent composer focus targets and the first single-tap File selection above the active keyboard. Check WebSocket peer closures, transport cancellation, and owner-task cancellation with python3 scripts/test-ios-sync-recovery.py; add --simulator to run the same real-socket checks with the iOS Foundation runtime. The simulator run also checks that automatic pending uploads preserve the foreground subscription while an explicit Sync now can restart it.

swift build --package-path apple --disable-sandbox --triple arm64-apple-ios17.0-simulator \
  --sdk "$(xcrun --sdk iphonesimulator --show-sdk-path)"
(cd android && ./gradlew test)
opam exec --switch=5.5.0 -- dune build @shared/test/runtest
opam exec --switch=5.5.0 -- dune build @shared/native/runtest
./scripts/test-android-e2e-runner.sh

For randomized multi-client sync checks, use Node.js 24 or newer. Build the current CLI bundle and db-worker-node in a separate Logseq master checkout, and build db-sync-node from its deps/db-sync directory. The CLI and worker must have matching revisions. Then build this repository's shared/native/logseq_sync_client.exe target and run:

node scripts/local-sync-fixture.cjs /absolute/path/to/logseq-master
# In another terminal, use the fixture path printed above:
node scripts/run-local-sync-stress.cjs /absolute/path/to/fixture.json 20261027 320 /absolute/path/to/logseq-master

The fixture runs the real local sync server with a disposable database, JWT issuer, and isolated CLI configuration. The driver uses the real mobile session and CLI, generates all mobile semantic operation types plus capture, transcription, and flashcard reviews, and checks actual attachment uploads. Runs of at least 177 iterations also exercise offline edits, process crashes, server restart, and lost WebSocket acknowledgements. Successful runs require empty pending queues and identical UUID-normalized synced datoms in the mobile database, CLI database, and a fresh server snapshot. The CLI comparison excludes its derived block/refs and local block/tx-id, matching the server checksum exclusions; the mobile/server comparison includes every datom. Each run retains its seed, operation trace, summary, and confirmed UI snapshot under the temporary fixture directory. Stop the fixture with Ctrl-C.

Authentication uses shared LUI forms and a shared OCaml Cognito state machine for email/password login, sign-up and email confirmation, password recovery, and MFA challenges. The native adapters call the Cognito User Pools JSON HTTPS API and keep tokens in Keychain (Apple) or Keystore-backed encrypted storage (Android). Neither client uses Hosted UI, a browser callback, Amplify, or an AWS SDK. The public Cognito app client must enable ALLOW_USER_PASSWORD_AUTH; session refresh uses GetTokensFromRefreshToken and sign-out uses RevokeToken.

Graph sync uses one WebSocket connection for dev's pull / pull/ok journal and tx/batch uploads. Pending graph transactions never use the HTTP upload API; attachments and snapshot transfers still use HTTP. Only canonical server journal rows enter the persisted graph; pending edits store normalized datoms and replay on a display DB without durable storage. Snapshot bootstrap reads its cursor through the existing pull endpoint and its schema from the snapshot database, without requiring extra snapshot metadata or response headers.

Pending graph edits upload in order, with at most 50 transactions and 1,000,000 UTF-8 bytes per JSON WebSocket message, including escaping and protocol fields. Dependencies created earlier in a batch can be used by later transactions. Deletes upload separately. Oversized individual transactions stay local with a sync error. Partial rejections settle the accepted prefix, mark the failed transaction, and preserve the unexecuted suffix for retry.

The normalization and overlay replay algorithms in Dev_sync are extracted verbatim from Logseq dev commit 53952776ef10805cba03009d444cf021f0d2c3ce. python3 scripts/vendor-dev-sync.py ../logseq-1 --check verifies the pinned source; the same command without --check regenerates it. Mobile platform adapters are kept in scripts/vendor-dev-sync-platform.ml.

Cognito configuration contains only the region, user pool ID, and public app client ID. Hosted UI domains, OAuth scopes, and callback URLs are not required.

About

Logseq mobile v2

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages