Wire Tauri AppImage to native keyring
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s

This commit is contained in:
2026-10-25 05:47:28 +01:00
parent 9854cc4c4f
commit 9167e04e77
17 changed files with 9347 additions and 1 deletions
+1
View File
@@ -1,5 +1,6 @@
node_modules/ node_modules/
native/target/ native/target/
src-tauri/target/
dist/ dist/
build/ build/
coverage/ coverage/
+231
View File
@@ -7,8 +7,12 @@
"": { "": {
"name": "trixy-linux-client", "name": "trixy-linux-client",
"version": "0.1.0-dev", "version": "0.1.0-dev",
"dependencies": {
"@tauri-apps/api": "^2.0.0"
},
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tauri-apps/cli": "^2.0.0",
"@tsconfig/svelte": "^5.0.0", "@tsconfig/svelte": "^5.0.0",
"prettier": "^3.5.0", "prettier": "^3.5.0",
"svelte": "^5.0.0", "svelte": "^5.0.0",
@@ -936,6 +940,233 @@
"vite": "^6.0.0" "vite": "^6.0.0"
} }
}, },
"node_modules/@tauri-apps/api": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.12.4.tgz",
"integrity": "sha512-pVmfis+4zT8H0Qhp3xPZx9Ju7kpzvA6rr/PSJPyuwfvucTQ8sVt61NYU9KMkK703UFlYadFmjKA4HhybOtN0gg==",
"license": "Apache-2.0 OR MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/tauri"
}
},
"node_modules/@tauri-apps/cli": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.12.4.tgz",
"integrity": "sha512-JI9bHfudSRJikGOrjbXEm6RjzzvmKuLc+JixB1NE8hNzQkNlCCIC3pTES3uypkR9lxHrsbxzcnSwhI2IxBQphw==",
"dev": true,
"license": "Apache-2.0 OR MIT",
"bin": {
"tauri": "tauri.js"
},
"engines": {
"node": ">= 10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/tauri"
},
"optionalDependencies": {
"@tauri-apps/cli-darwin-arm64": "2.12.4",
"@tauri-apps/cli-darwin-x64": "2.12.4",
"@tauri-apps/cli-linux-arm-gnueabihf": "2.12.4",
"@tauri-apps/cli-linux-arm64-gnu": "2.12.4",
"@tauri-apps/cli-linux-arm64-musl": "2.12.4",
"@tauri-apps/cli-linux-riscv64-gnu": "2.12.4",
"@tauri-apps/cli-linux-x64-gnu": "2.12.4",
"@tauri-apps/cli-linux-x64-musl": "2.12.4",
"@tauri-apps/cli-win32-arm64-msvc": "2.12.4",
"@tauri-apps/cli-win32-ia32-msvc": "2.12.4",
"@tauri-apps/cli-win32-x64-msvc": "2.12.4"
}
},
"node_modules/@tauri-apps/cli-darwin-arm64": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.12.4.tgz",
"integrity": "sha512-OxqKSckIkoJ6vX2AjifIJpTasFC0Hbdoo7gEiCmAjauABO3U8m2MRsLWe+QTiBwDx1mIrvgA+rvI7tvDnbnHVg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-darwin-x64": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.12.4.tgz",
"integrity": "sha512-fz76/77wMMJOaaUN6n3CybRG0P/MTEjjqYk81mGPyhi9m6Ue5T+LWBnJP/AkPooNatBPI2vtulGqomyMJ44lCQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.12.4.tgz",
"integrity": "sha512-K0w8Bof6Lt8QKLAYhRy0yy36ZH5iA/35VuSOvnxyjvCUZ+A3JKsUTlALviH/sVgAlisaPzzmeJSH8XOn4Q2HBA==",
"cpu": [
"arm"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.12.4.tgz",
"integrity": "sha512-j5rOnjmRUwWrm1oosf0zBYl4D+fqydgUROFCk0hvKZxvbTT8SOERes0QB7rBV/XWNy+kM6pCKzb3oSm2sRglzg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.12.4.tgz",
"integrity": "sha512-aOue3Fy6Jb7+oWcfAVUsVHfeTybSCt2CZoU06aO4jv4d2Ks/KnaTPwMAK3CLEduGOvBLY1lbBBrIyomqLLH5Sg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.12.4.tgz",
"integrity": "sha512-Fx6GShY/RvST+t6LMlTHAwd66+1tEaAAGrnU7cysVfaiRP1BKng5Vl6rVmO6rSLOA1Sp1dRzNYM4f0OWQGb7qA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.12.4.tgz",
"integrity": "sha512-tNzZSa79Z1nRpHxEpQ38lsAFpKlFlYvoXzjTV+lRRn/zSf3wrdUC3wnLcq2XEadchPu4ttK0zivhYzr+XGD19Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-musl": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.12.4.tgz",
"integrity": "sha512-jmZ1pr0xEqUhuEusWtt9Yt+GF6lCnGHUmIL+KIknpbjK/XQ2qkhDBhaxK/JMHM2A8dAacVXCxXJk2EXpwhQUfA==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.12.4.tgz",
"integrity": "sha512-fGo8pgEHZxdSYgZ/J98m1usJ7NHKmKbq7k6uT0x1rpOq6Bg2YVzzpaAblWk710f5LHYBJIL5QIFS5IrD+4gmTg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.12.4.tgz",
"integrity": "sha512-ezl8sQIcjYsye3TUnTH5VcWmWzccZASlQm7hlZt0dYyj1XD0qpVW47u2xYVrIKui0F8uQv/7QGZixdWiClBsbg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
"version": "2.12.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.12.4.tgz",
"integrity": "sha512-DJhDeCqjLDNMWYFy7ZS8cfeAHX2RK0RbQMLiA3bMVVkXRDJ4NBYiHriGqsUhsr9iG41aTb+dJGkFPmPw1gWwVw==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tsconfig/svelte": { "node_modules/@tsconfig/svelte": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.8.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.8.tgz",
+7
View File
@@ -5,14 +5,21 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build",
"build": "vite build", "build": "vite build",
"check": "svelte-check --tsconfig ./tsconfig.json", "check": "svelte-check --tsconfig ./tsconfig.json",
"format": "prettier --write .", "format": "prettier --write .",
"format:check": "prettier --check ." "format:check": "prettier --check ."
}, },
"dependencies": {
"@tauri-apps/api": "^2.0.0"
},
"devDependencies": { "devDependencies": {
"@tsconfig/svelte": "^5.0.0", "@tsconfig/svelte": "^5.0.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tauri-apps/cli": "^2.0.0",
"prettier": "^3.5.0", "prettier": "^3.5.0",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
+1
View File
@@ -0,0 +1 @@
target/
+4305
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "trixy-linux-client"
version = "0.1.0"
description = "Trixie Linux client"
authors = ["ReaperOfVeriod"]
license = "MIT"
edition = "2021"
[lib]
name = "trixy_linux_client_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri = { version = "2", features = [] }
trixy-native = { path = "../native" }
+3
View File
@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<rect width="128" height="128" rx="20" fill="#081018"/>
<path d="M64 18 104 64 64 110 24 64Z" fill="none" stroke="#00b9f2" stroke-width="8"/>
<path d="M64 36 88 64 64 92 40 64Z" fill="#182b3a" stroke="#00b9f2" stroke-width="4"/>
</svg>

After

Width:  |  Height:  |  Size: 305 B

+65
View File
@@ -0,0 +1,65 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::path::PathBuf;
use trixy_native::{clear_session, default_settings_path, load_session, store_session, Settings};
fn error_message(error: std::io::Error) -> String {
error.to_string()
}
#[tauri::command]
fn load_client_settings() -> Result<Settings, String> {
let path = default_settings_path();
if !path.exists() {
return Ok(Settings {
server_address: "http://127.0.0.1:8787".to_string(),
username: String::new(),
});
}
Settings::load(path).map_err(error_message)
}
#[tauri::command]
fn save_client_settings(settings: Settings) -> Result<(), String> {
if settings.server_address.trim().is_empty() || settings.username.trim().is_empty() {
return Err("Server address and username are required.".to_string());
}
Settings::save(&settings, default_settings_path()).map_err(error_message)
}
#[tauri::command]
fn load_server_session(settings: Settings) -> Result<Option<String>, String> {
load_session(&settings).map_err(error_message)
}
#[tauri::command]
fn store_server_session(settings: Settings, session: String) -> Result<(), String> {
store_session(&settings, &session).map_err(error_message)
}
#[tauri::command]
fn clear_server_session(settings: Settings) -> Result<(), String> {
clear_session(&settings).map_err(error_message)
}
#[tauri::command]
fn client_settings_path() -> String {
PathBuf::from(default_settings_path())
.to_string_lossy()
.into_owned()
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
load_client_settings,
save_client_settings,
load_server_session,
store_server_session,
clear_server_session,
client_settings_path
])
.run(tauri::generate_context!())
.expect("error while running Trixie client");
}
+3
View File
@@ -0,0 +1,3 @@
fn main() {
trixy_linux_client_lib::run();
}
+35
View File
@@ -0,0 +1,35 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Trixie",
"version": "0.1.0",
"identifier": "nl.reaperofveriod.trixie",
"build": {
"beforeDevCommand": "npm run dev -- --host 127.0.0.1",
"devUrl": "http://127.0.0.1:5173",
"beforeBuildCommand": "npm run build",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "Trixie",
"width": 1366,
"height": 768,
"minWidth": 1024,
"minHeight": 640,
"resizable": true
}
],
"security": {
"csp": "default-src 'self' http://127.0.0.1:8787 ws://127.0.0.1:8787 https://*.twitch.tv https://*.twitchcdn.net https://cdn.7tv.app; connect-src 'self' http: https: ws: wss:; img-src 'self' data: https:; frame-src https://player.twitch.tv; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'"
}
},
"bundle": {
"active": true,
"icon": ["icons/icon.png", "icons/icon.svg"],
"targets": ["appimage"],
"linux": {
"deb": { "depends": [] }
}
}
}
+37 -1
View File
@@ -1,5 +1,35 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { invoke } from '@tauri-apps/api/core';
type ClientSettings = { server_address: string; username: string };
function nativeClientAvailable() {
return typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window;
}
async function saveNativeSession() {
if (!nativeClientAvailable() || !username || !serverAddress || !sessionToken) return;
const settings: ClientSettings = { server_address: serverAddress, username };
await invoke('save_client_settings', { settings });
await invoke('store_server_session', { settings, session: sessionToken });
}
async function clearNativeSession() {
if (!nativeClientAvailable() || !username || !serverAddress) return;
await invoke('clear_server_session', { settings: { server_address: serverAddress, username } satisfies ClientSettings });
}
async function loadNativeSettings() {
if (!nativeClientAvailable()) return;
try {
const settings = await invoke<ClientSettings>('load_client_settings');
serverAddress = settings.server_address;
username = settings.username;
} catch {
authError = 'Native settings could not be read. Enter the server address and username again.';
}
}
let serverAddress = 'http://127.0.0.1:8787'; let serverAddress = 'http://127.0.0.1:8787';
let connectionState = 'OFFLINE'; let connectionState = 'OFFLINE';
@@ -205,6 +235,9 @@
sessionToken = result.session_token; sessionToken = result.session_token;
authenticated = true; authenticated = true;
mustReplaceCredentials = result.must_replace_credentials; mustReplaceCredentials = result.must_replace_credentials;
await saveNativeSession().catch(() => {
authError = 'Login succeeded, but the session could not be stored in the Linux keyring.';
});
connectionState = 'CONNECTED'; connectionState = 'CONNECTED';
await loadActivity(); await loadActivity();
await loadNotifications(); await loadNotifications();
@@ -267,6 +300,7 @@
sessionToken = ''; sessionToken = '';
authenticated = false; authenticated = false;
mustReplaceCredentials = false; mustReplaceCredentials = false;
await clearNativeSession().catch(() => undefined);
username = newUsername; username = newUsername;
password = ''; password = '';
newPassword = ''; newPassword = '';
@@ -285,6 +319,7 @@
if (sessionToken) { if (sessionToken) {
await fetch(`${serverAddress}/api/v1/auth/logout`, { method: 'POST', headers: { authorization: `Bearer ${sessionToken}` } }).catch(() => undefined); await fetch(`${serverAddress}/api/v1/auth/logout`, { method: 'POST', headers: { authorization: `Bearer ${sessionToken}` } }).catch(() => undefined);
} }
await clearNativeSession().catch(() => undefined);
sessionToken = ''; sessionToken = '';
authenticated = false; authenticated = false;
mustReplaceCredentials = false; mustReplaceCredentials = false;
@@ -1026,7 +1061,8 @@
}); });
} }
onMount(() => { onMount(async () => {
await loadNativeSettings();
void checkServer(); void checkServer();
void loadPublicCommands(); void loadPublicCommands();
}); });