Add native self-hosted instance connection to fluxer_desktop
Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 + tools/ci) with a "Connect to a Different Server" menu item and popout that lets the desktop app switch to any self-hosted Fluxer instance, plus fixes for well-known discovery on single-domain self-hosted deployments and a false-positive ERR_ABORTED on same-origin client redirects during the switch. Defaults to chat.fluxr.chat and uses an isolated userData directory from the official build.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
pub mod probe_helpers;
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
pub fn vendor_name(vendor_id: u32) -> Option<&'static str> {
|
||||
match vendor_id {
|
||||
0x1002 | 0x1022 => Some("AMD"),
|
||||
0x106b => Some("Apple"),
|
||||
0x10de => Some("NVIDIA"),
|
||||
0x1234 => Some("QEMU"),
|
||||
0x1414 => Some("Microsoft"),
|
||||
0x15ad => Some("VMware"),
|
||||
0x1af4 => Some("Virtio"),
|
||||
0x1b36 => Some("QEMU"),
|
||||
0x5143 => Some("Qualcomm"),
|
||||
0x8086 => Some("Intel"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vendor_id_from_name(name: &str) -> u32 {
|
||||
let lower = name.to_ascii_lowercase();
|
||||
if lower.contains("apple") {
|
||||
return 0x106b;
|
||||
}
|
||||
if lower.contains("amd") || lower.contains("radeon") {
|
||||
return 0x1002;
|
||||
}
|
||||
if lower.contains("intel") {
|
||||
return 0x8086;
|
||||
}
|
||||
if lower.contains("nvidia") {
|
||||
return 0x10de;
|
||||
}
|
||||
if lower.contains("microsoft") {
|
||||
return 0x1414;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn write_hex_u64(value: u64) -> String {
|
||||
format!("0x{value:x}")
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ParseHexIdError {
|
||||
InvalidHexId,
|
||||
InvalidDigit,
|
||||
}
|
||||
|
||||
pub fn parse_hex_id(raw: &str) -> Result<u32, ParseHexIdError> {
|
||||
let mut trimmed = raw.trim();
|
||||
if let Some(rest) = trimmed
|
||||
.strip_prefix("0x")
|
||||
.or_else(|| trimmed.strip_prefix("0X"))
|
||||
{
|
||||
trimmed = rest;
|
||||
}
|
||||
if trimmed.is_empty() || trimmed.len() > 8 {
|
||||
return Err(ParseHexIdError::InvalidHexId);
|
||||
}
|
||||
u32::from_str_radix(trimmed, 16).map_err(|_| ParseHexIdError::InvalidDigit)
|
||||
}
|
||||
|
||||
pub fn is_drm_card_name(name: &str) -> bool {
|
||||
let Some(rest) = name.strip_prefix("card") else {
|
||||
return false;
|
||||
};
|
||||
!rest.is_empty() && rest.bytes().all(|ch| ch.is_ascii_digit())
|
||||
}
|
||||
|
||||
pub fn basename(path: &str) -> &str {
|
||||
path.rsplit_once('/').map_or(path, |(_, base)| base)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_hex_id_accepts_sysfs_hex_ids() {
|
||||
assert_eq!(0x8086, parse_hex_id("0x8086\n").unwrap());
|
||||
assert_eq!(0x10de, parse_hex_id("10DE").unwrap());
|
||||
assert_eq!(Err(ParseHexIdError::InvalidHexId), parse_hex_id("0x"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_drm_card_name_accepts_cards_but_rejects_connectors_render_nodes() {
|
||||
assert!(is_drm_card_name("card0"));
|
||||
assert!(is_drm_card_name("card12"));
|
||||
assert!(!is_drm_card_name("card0-DP-1"));
|
||||
assert!(!is_drm_card_name("renderD128"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vendor_id_helpers() {
|
||||
assert_eq!(0x106b, vendor_id_from_name("Apple M3 GPU"));
|
||||
assert_eq!(Some("NVIDIA"), vendor_name(0x10de));
|
||||
assert_eq!(None, vendor_name(0xffff));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user