Datler ships as a Tauri v2 app: the Rust shell hosts a WebView that loads the built renderer, and the same project builds for Windows and Android. The browser build is a third way to run the same renderer; see Synced Databases.
A release build serves the renderer’s built files (frontendDist) rather than the dev server, so some behaviour shows only there. The app is wrapped in React’s StrictMode, which in a dev build runs effects twice and can hide an ordering bug that a release build exposes, so a change to anything timing-sensitive is checked in a release build.
Windows
pnpm run build runs tauri build, which builds the renderer first and bundles every target for the platform: an MSI installer and an NSIS setup .exe. The app is Datler, identifier ca.datler.app, which also names the folder its profiles live in (Profiles). There is no code signing, no store listing and no auto-update. The landing page’s download card offers the Windows .exe and the Android .apk attached to the latest GitHub release, when there is one (Public Site).
The window opens at 1280 × 720 and can be no smaller than 360 × 520. With DATLER_BACKGROUND=1 set, a desktop dev run opens the window behind the others without taking focus, so a script that relaunches and screenshots the app does not take the keyboard from whoever is typing (Driving the App (CDP)); unset, it changes nothing.
Android
The Android project is generated by tauri android init into apps/desktop/src-tauri/gen/android and committed. Building it needs a one-time toolchain (JDK 17, the Android SDK and NDK, three environment variables, the Rust targets): see Android Toolchain Setup. The phone runs debug builds (pnpm run tauri android build --debug --target aarch64), which Android installs without a release keystore; a release build is unsigned and will not install. The renderer needs nothing of its own for Android: the SQL plugin resolves the relative sqlite:<file> per platform, and the layout is responsive. The drive harness is not set up for the Android app; a debug build’s WebView is inspected over USB with adb and chrome://inspect.
The Activity owns the system bars and the keyboard. Android draws edge to edge, so without help the WebView sits under the status bar and the gesture bar and their taps are swallowed. MainActivity.kt consumes the insets once, as padding on the view that hosts the WebView: the system bars and any display cutout on every side, and at the bottom the larger of the gesture bar and the keyboard. So the page always ends above the keyboard, the browser scrolls a focused field into view, and nothing in the web layer has to know system bars exist. It is done here rather than in CSS because fixed-position elements (the tab bar, the drawer, overlays) resolve against the viewport, not an ancestor’s padding, so in CSS every one of them would need its own safe-area rule.
The strips the padding reserves show the window’s background. The page reports the colour it paints through a bridge the Activity registers when the WebView is made (DatlerSystemBars; apps/renderer/src/lib/system-bars.ts sends it at boot and whenever the theme on <html> or the system’s mode changes), and the Activity sets the window’s background to it and turns the status and gesture bars’ icons dark on a light page, so the strips follow every mode and palette. Until the page reports, they show the theme’s windowBackground, the dark default’s #0e1116. MainActivity.kt and res/values/themes.xml are not among the files Tauri regenerates, so they are edited by hand.
The shell’s surface
- Two plugins:
tauri-plugin-sql(SQLite) and the localscanplugin (apps/desktop/plugins/scan) for barcodes and labels on Android (Reading a Label). Nothing else is registered or shipped. - Six Rust commands:
sql_batch, which runs several statements as one unit on one connection (Repository Seam), reading and writing the profile state file, deleting a profile’s file (only auser-<slug>.dbname, with its-wal,-shmand-journal), saving an export’s files to a new folder in Downloads, and opening that folder (only a folder inside Downloads) (Profiles).- One capability, for the window labelledmain, granting Tauri’s core defaults, the SQL plugin’s defaults with load, execute, select and close, and the scan plugin’s defaults. The window label is required: without it the capability matches no window and the SQL plugin refuses to load. Android labels its WebViewmaintoo. - A content security policy: everything
'self'; images also fromdata:and Tauri’s asset protocol, and fonts fromdata:(the bundled fonts are inlined as data URIs); inline styles allowed, since the charts set style attributes; andconnect-srcnaming exactly what the app talks to: Tauri’s IPC, the sync service on localhost, staging and production (HTTP and WebSocket), Open Food Facts and NASA POWER. A new outside service is added here or the built app cannot reach it, while the dev build, which does not enforce the policy, can.
Versions
The app’s version is written in four files, kept equal: the root package.json, apps/desktop/package.json, apps/desktop/src-tauri/Cargo.toml and apps/desktop/src-tauri/tauri.conf.json, whose version is the one Tauri stamps on the build. pnpm run bump (bump.js) raises the patch in all four, or bump minor, bump major or an exact bump 1.2.3. It edits each file in place, so their formatting stays as it was, and refuses when the four already differ.
Code
apps/desktop/src-tauri/tauri.conf.json— the identifier, the window, the bundle and the content security policy.apps/desktop/src-tauri/capabilities/default.json,apps/desktop/src-tauri/src/lib.rs,apps/desktop/src-tauri/Cargo.toml,apps/desktop/plugins/scan.apps/desktop/src-tauri/gen/android/app/src/main/java/ca/datler/app/MainActivity.ktand itsres/values/themes.xml.
Related
Android Toolchain Setup · Architecture Overview · Synced Databases · Driving the App (CDP) · Public Site