Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d7883e8
chore(tauri): add initial setup
hazre Mar 5, 2026
06d3c79
feat(tauri): disable web push notifications
hazre Mar 5, 2026
e01c01c
feat(tauri): use deep link for sso authentication
hazre Mar 5, 2026
6932dcb
feat(tauri): custom windows titlebar
hazre Mar 5, 2026
2e10f73
feat(tauri): add desktop system tray and option to close to tray
hazre Mar 5, 2026
9e4a814
fix(tauri): ignore formatting on generated tauri bindings
hazre Mar 6, 2026
5823d23
fix(tauri): deep linking breaking due to specta export
hazre Mar 6, 2026
80dce7a
feat(tauri): add sso callback page
hazre Mar 6, 2026
84e71cf
feat(tauri): keep track of window state and position
hazre Mar 6, 2026
535ded2
chore: add tauri.conf.json to knope.toml
hazre Mar 6, 2026
55da9f1
style: fix formatting of Router.tsx
hazre Mar 7, 2026
b709f38
feat(tauri): add android support and splash screen tauri plugin (WIP)
hazre Mar 7, 2026
782626a
chore: add crates to knip ignore list
hazre Mar 7, 2026
46e5650
fix: broken Router and linting issues
hazre Mar 7, 2026
880efc4
chore: ignore false positive for tauri-plugin-splashscreen-api in knip
hazre Mar 8, 2026
fd3f341
fix: merge conflicts in package-lock.json
hazre Mar 8, 2026
cb0413c
chore: remove accidentally committed build files and adjusted .gitignore
TastelessVoid Mar 9, 2026
f0a9f4f
fix: bypass service worker for media authentication on Android
TastelessVoid Mar 9, 2026
751c2fc
fix(build): allow PWA build with large wasm assets and split vendor c…
hazre Mar 8, 2026
46092a8
feat: Add UnifiedPush for Fairphones or phones that have been "de-goo…
TastelessVoid Mar 9, 2026
f5d90c0
feat: Add build.gradle.kts to specify a higher kotlin version and upd…
TastelessVoid Mar 9, 2026
0aa9cf8
fix: Commit the gen folder as the nested .gitignore should keep the r…
TastelessVoid Mar 9, 2026
f9f37a1
fix: Adjusted .gitignore and tracked files
TastelessVoid Mar 9, 2026
daeec60
feat(tauri): Implement media caching and notification enhancements
TastelessVoid Mar 10, 2026
ade84fa
feat(tauri): Addressed review, updated package
TastelessVoid Mar 10, 2026
c064680
feat(tauri): Fixed dynamical import warning, removed local crate due …
TastelessVoid Mar 10, 2026
2d8609b
fix(tauri): Fixed avatars in the notification not showing
TastelessVoid Mar 11, 2026
9c84d92
Merge branch 'dev' into feat/tauri-integration
TastelessVoid Mar 11, 2026
93bca4b
chore(tauri): Merge dev into feat/tauri-integration and resolved conf…
TastelessVoid Mar 11, 2026
0b97f55
Merge pull request #139 from TastelessVoid/feat/tauri-integration
hazre Mar 12, 2026
65e83ed
fix: make android actually build
hazre Mar 12, 2026
8667938
chore: merge dev into feat/tauri-integration
hazre Mar 12, 2026
abfaab5
chore: make quality checks happy
hazre Mar 12, 2026
fd6a135
chore: remove binary files
hazre Mar 12, 2026
bf36d2b
fix: sso login on mobile
hazre Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,27 @@ devAssets
*.tfbackend
!*.tfbackend.example
crash.log

# Tauri
/build
/target/
/gen/schemas
dist-js

# Gradle / Android build artifacts
**/android/build/
**/android/.tauri/
**/android/.gradle/
**/.gradle/

# Rust build artifacts
src-tauri/target/
# Tauri Android build outputs
src-tauri/gen/android/app/build/


# Rust build artifacts in subcrates
crates/**/target/

# Worktrees
.worktrees
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ pnpm-lock.yaml
LICENSE
README.md
CHANGELOG.md
./changeset
./changeset
src/app/generated
crates/
src-tauri/
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
}
}
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"pushNotificationDetails": {
"pushNotifyUrl": "https://cinny.cc/_matrix/push/v1/notify",
"vapidPublicKey": "BHLwykXs79AbKNiblEtZZRAgnt7o5_ieImhVJD8QZ01MVwAHnXwZzNgQEJJEU3E5CVsihoKtb7yaNe5x3vmkWkI",
"webPushAppID": "cc.cinny.web"
"webPushAppID": "cc.cinny.web",
"unifiedPushAppID": "moe.sable.up"
},

"slidingSync": {
Expand Down
12 changes: 12 additions & 0 deletions crates/sable-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "sable-macros"
version = "0.1.0"
edition = "2024"

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full"] }
154 changes: 154 additions & 0 deletions crates/sable-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
parse_macro_input, Attribute, Path, Token,
};

struct CommandItem {
/// The tokens inside `#[cfg(...)]`, e.g. `desktop` or `target_os = "windows"`.
/// `None` means the command is always compiled in.
cfg_tokens: Option<TokenStream2>,
path: Path,
}

struct CommandList(Vec<CommandItem>);

impl Parse for CommandList {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut items = vec![];
while !input.is_empty() {
let attrs = Attribute::parse_outer(input)?;
let path: Path = input.parse()?;

// Extract the first #[cfg(...)] attribute if present.
// Any other attributes are ignored (they wouldn't make sense here anyway).
let cfg_tokens = attrs.iter().find_map(|attr| {
if !attr.path().is_ident("cfg") {
return None;
}
attr.meta
.require_list()
.ok()
.map(|list| list.tokens.clone())
});

items.push(CommandItem { cfg_tokens, path });

// Consume optional trailing comma
if input.peek(Token![,]) {
let _ = input.parse::<Token![,]>();
}
}
Ok(CommandList(items))
}
}

/// A drop-in replacement for `tauri_specta::collect_commands!` that supports
/// `#[cfg(...)]` attributes on individual commands.
///
/// # Example
/// ```rust
/// collect_commands![
/// #[cfg(desktop)]
/// desktop_tray::set_close_to_tray_enabled,
/// windows::snap_overlay::show_snap_overlay,
/// windows::snap_overlay::hide_snap_overlay,
/// ]
/// ```
///
/// # How it works
///
/// For each unique cfg predicate P found in the list the macro generates two
/// complete `tauri_specta::internal::command(generate_handler![...],
/// collect_functions![...])` calls — one for `#[cfg(P)]` (including those
/// commands) and one for `#[cfg(not(P))]` (excluding them). The compiler
/// picks exactly one branch per target, so every command path only needs to
/// exist on the targets where its cfg condition is true.
///
/// For N distinct predicates, 2^N branches are emitted. In practice only
/// `#[cfg(desktop)]` is used so this is always just two branches.
#[proc_macro]
pub fn collect_commands(input: TokenStream) -> TokenStream {
let CommandList(items) = parse_macro_input!(input as CommandList);

// Collect the unique cfg predicates present in this invocation.
let mut predicates: Vec<TokenStream2> = vec![];
for item in &items {
if let Some(cfg) = &item.cfg_tokens {
let key = cfg.to_string();
if !predicates
.iter()
.any(|p: &TokenStream2| p.to_string() == key)
{
predicates.push(cfg.clone());
}
}
}

let n = predicates.len();
let num_variants = 1usize << n; // 2^n — always at least 1

let mut branches: Vec<TokenStream2> = vec![];

for variant in 0..num_variants {
// For variant `v`, bit `i` being set means predicate[i] is "active"
// (true) for this branch.

// Build `#[cfg(all(pred0_or_not, pred1_or_not, ...))]`
let conditions: Vec<TokenStream2> = predicates
.iter()
.enumerate()
.map(|(i, pred)| {
if variant & (1 << i) != 0 {
quote! { #pred }
} else {
quote! { not(#pred) }
}
})
.collect();

let cfg_guard: TokenStream2 = if conditions.is_empty() {
// No predicates at all — unconditional (wrapping in all() is valid).
quote! {}
} else {
quote! { #[cfg(all(#(#conditions),*))] }
};

// Collect commands that are visible in this variant:
// - always-on commands (no cfg attribute) are always included
// - cfg-gated commands are included only when their predicate bit is set
let variant_paths: Vec<&Path> = items
.iter()
.filter(|item| match &item.cfg_tokens {
None => true, // always-on
Some(cfg) => {
let key = cfg.to_string();
let idx = predicates
.iter()
.position(|p| p.to_string() == key)
.unwrap();
variant & (1 << idx) != 0
}
})
.map(|item| &item.path)
.collect();

branches.push(quote! {
#cfg_guard
let __commands = ::tauri_specta::internal::command(
::tauri::generate_handler![#(#variant_paths),*],
::specta::function::collect_functions![#(#variant_paths),*],
);
});
}

quote! {
{
#(#branches)*
__commands
}
}
.into()
}
16 changes: 16 additions & 0 deletions crates/tauri-plugin-splashscreen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/.vs
.DS_Store
.Thumbs.db
*.sublime*
.idea/
debug.log
package-lock.json
.vscode/settings.json
yarn.lock

/.tauri
/.gradle
/target
Cargo.lock
node_modules/

17 changes: 17 additions & 0 deletions crates/tauri-plugin-splashscreen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "tauri-plugin-splashscreen"
version = "0.1.0"
authors = [ "You" ]
description = ""
edition = "2021"
rust-version = "1.77.2"
exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"]
links = "tauri-plugin-splashscreen"

[dependencies]
tauri = { version = "2.10.3" }
serde = "1.0"
thiserror = "2"

[build-dependencies]
tauri-plugin = { version = "2.5.4", features = ["build"] }
1 change: 1 addition & 0 deletions crates/tauri-plugin-splashscreen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Tauri Plugin splashscreen
3 changes: 3 additions & 0 deletions crates/tauri-plugin-splashscreen/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/build
/.tauri
/.gradle
45 changes: 45 additions & 0 deletions crates/tauri-plugin-splashscreen/android/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}

android {
namespace = "moe.sable.app.plugin.splashscreen"
compileSdk = 36

defaultConfig {
minSdk = 21

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {

implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.core:core-splashscreen:1.2.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation(project(":tauri-android"))
}
21 changes: 21 additions & 0 deletions crates/tauri-plugin-splashscreen/android/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
31 changes: 31 additions & 0 deletions crates/tauri-plugin-splashscreen/android/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
google()
}
resolutionStrategy {
eachPlugin {
switch (requested.id.id) {
case "com.android.library":
useVersion("8.0.2")
break
case "org.jetbrains.kotlin.android":
useVersion("1.8.20")
break
}
}
}
}

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
google()

}
}

include ':tauri-android'
project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package moe.sable.app.plugin.splashscreen

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("moe.sable.app.plugin.splashscreen", appContext.packageName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
Loading
Loading