From d893b461d95f6feb03250354e6d86d8f099f199b Mon Sep 17 00:00:00 2001 From: AteebNoOne Date: Sun, 12 Jul 2026 17:19:02 +0500 Subject: [PATCH 1/7] feat: add Snap Store badge and web app URL in README and landing pages Co-authored-by: Atib Ur Rehman --- README.md | 4 +++ src/pages/LandingPage.tsx | 47 ++++++++++++++++++++++---------- teebot_flow_landing_premium.html | 3 ++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f84b7c3..d7390e6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # 🌊 Teebot Flow +[![teebot-flow](https://snapcraft.io/teebot-flow/badge.svg)](https://snapcraft.io/teebot-flow) + +🌐 **Web Version**: [flow.iteebot.com](https://flow.iteebot.com) + **Teebot Flow** is a high-performance, professional Desktop ERP solution designed for modern business operations. Built with a focus on speed, reliability, and ease of use, it leverages the power of **Tauri** and **Rust** to provide a secure, offline-first desktop experience. Teebot Flow is now open for public collaboration. The project is released under **AGPL-3.0**, and contributions, issue reports, and pull requests are welcome. diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx index cdb56a8..837ae62 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -166,20 +166,39 @@ export function LandingPage() {
{platform.icon}

{platform.os}

- {platform.downloads.map((dl) => ( - -
- {dl.name} - -
-
- ))} + {platform.downloads.map((dl) => { + if (dl.name === "Snap Store") { + return ( + + Get it from the Snap Store + + ); + } + return ( + +
+ {dl.name} + +
+
+ ); + })}
))} diff --git a/teebot_flow_landing_premium.html b/teebot_flow_landing_premium.html index 6f0481f..8f931b0 100644 --- a/teebot_flow_landing_premium.html +++ b/teebot_flow_landing_premium.html @@ -207,6 +207,9 @@

Business management, built for speed

RPM Package
~6.5 MB
+ + Get it from the Snap Store +
From 35db2ead559cb294bfaeec8e79f5e95bf8ea2c21 Mon Sep 17 00:00:00 2001 From: AteebNoOne Date: Sun, 12 Jul 2026 17:24:33 +0500 Subject: [PATCH 2/7] feat: add RPM package for Linux and style Snap Store badge to full width Co-authored-by: Atib Ur Rehman --- src/lib/platform.ts | 5 +++++ src/pages/LandingPage.tsx | 4 ++-- teebot_flow_landing_premium.html | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/lib/platform.ts b/src/lib/platform.ts index 3072e8a..3c1fb3f 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -45,6 +45,7 @@ const ASSET_NAMES = { linux: { appImage: (version: string) => `Teebot Flow_${version}_amd64.AppImage`, deb: (version: string) => `Teebot Flow_${version}_amd64.deb`, + rpm: (version: string) => `Teebot.Flow-${version}-1.x86_64.rpm`, }, macOS: { aarch64: (version: string) => `Teebot.Flow_${version}_aarch64.dmg`, @@ -111,6 +112,10 @@ export const getReleaseDownloads = (version: string) => ({ name: "Debian Package (.deb)", url: getDownloadLink(version, ASSET_NAMES.linux.deb(version)), }, + { + name: "Red Hat Package (.rpm)", + url: getDownloadLink(version, ASSET_NAMES.linux.rpm(version)), + }, { name: "Snap Store", url: SNAP_STORE_URL, diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx index 837ae62..5d1cd96 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -174,12 +174,12 @@ export function LandingPage() { href={dl.url} target="_blank" rel="noopener noreferrer" - className="inline-block mt-2 hover:opacity-90 transition-opacity" + className="block w-full mt-2 hover:opacity-90 transition-opacity" > Get it from the Snap Store ); diff --git a/teebot_flow_landing_premium.html b/teebot_flow_landing_premium.html index 8f931b0..94a8131 100644 --- a/teebot_flow_landing_premium.html +++ b/teebot_flow_landing_premium.html @@ -207,8 +207,8 @@

Business management, built for speed

RPM Package
~6.5 MB
- - Get it from the Snap Store + + Get it from the Snap Store
From 448dfb371e4a0a142e1543567533179c51f15e14 Mon Sep 17 00:00:00 2001 From: AteebNoOne Date: Sun, 12 Jul 2026 17:25:46 +0500 Subject: [PATCH 3/7] feat: add target and rel attributes to snap store link in premium landing page Co-authored-by: Atib Ur Rehman --- teebot_flow_landing_premium.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/teebot_flow_landing_premium.html b/teebot_flow_landing_premium.html index 94a8131..8db8f7f 100644 --- a/teebot_flow_landing_premium.html +++ b/teebot_flow_landing_premium.html @@ -207,7 +207,7 @@

Business management, built for speed

RPM Package
~6.5 MB
- + Get it from the Snap Store
From 52be0f51571f28484ae079aff42496244b9cd980 Mon Sep 17 00:00:00 2001 From: AteebNoOne Date: Sun, 12 Jul 2026 17:29:37 +0500 Subject: [PATCH 4/7] fix: prioritise WebGL detection for macOS Apple Silicon architecture to bypass masked user-agents Co-authored-by: Atib Ur Rehman --- src/lib/platform.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/lib/platform.ts b/src/lib/platform.ts index 3c1fb3f..6385a8c 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -55,15 +55,8 @@ const ASSET_NAMES = { const getMacArch = (): "aarch64" | "x64" | null => { if (typeof window === "undefined") return null; - const userAgent = window.navigator.userAgent.toLowerCase(); - - if (userAgent.includes("arm64") || userAgent.includes("aarch64")) { - return "aarch64"; - } - if (userAgent.includes("intel") || userAgent.includes("x64") || userAgent.includes("x86_64")) { - return "x64"; - } + // 1. Try WebGL detection first (highly accurate for Apple Silicon GPUs, bypassing masked user-agent strings) try { const canvas = document.createElement("canvas"); const gl = (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")) as WebGLRenderingContext | null; @@ -83,7 +76,16 @@ const getMacArch = (): "aarch64" | "x64" | null => { } } } catch (e) { - // Ignore fallback + // Proceed to fallback + } + + // 2. Fallback to User Agent checks + const userAgent = window.navigator.userAgent.toLowerCase(); + if (userAgent.includes("arm64") || userAgent.includes("aarch64")) { + return "aarch64"; + } + if (userAgent.includes("intel") || userAgent.includes("x64") || userAgent.includes("x86_64")) { + return "x64"; } return null; From b21a4c26cd0d90e9bc4630be057124401e87d90e Mon Sep 17 00:00:00 2001 From: AteebNoOne Date: Sun, 12 Jul 2026 17:31:09 +0500 Subject: [PATCH 5/7] feat: detect Linux distribution to serve .rpm or .deb download as best action Co-authored-by: Atib Ur Rehman --- src/lib/platform.ts | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/lib/platform.ts b/src/lib/platform.ts index 6385a8c..7fade46 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -135,6 +135,35 @@ export const getReleaseDownloads = (version: string) => ({ ], }); +const detectLinuxDistro = (): "debian" | "redhat" | "generic" => { + if (typeof window === "undefined") return "generic"; + const userAgent = window.navigator.userAgent.toLowerCase(); + + if ( + userAgent.includes("ubuntu") || + userAgent.includes("debian") || + userAgent.includes("mint") || + userAgent.includes("pop!_os") || + userAgent.includes("elementary") + ) { + return "debian"; + } + + if ( + userAgent.includes("fedora") || + userAgent.includes("red hat") || + userAgent.includes("redhat") || + userAgent.includes("centos") || + userAgent.includes("suse") || + userAgent.includes("rocky") || + userAgent.includes("alma") + ) { + return "redhat"; + } + + return "generic"; +}; + export const getBestDownloadForOS = (os: string, version: string) => { const tagUrl = `${GITHUB_RELEASES_URL}/tag/v${version}`; switch (os) { @@ -161,11 +190,19 @@ export const getBestDownloadForOS = (os: string, version: string) => { url: tagUrl, }; } - case "Linux": + case "Linux": { + const distro = detectLinuxDistro(); + if (distro === "redhat") { + return { + label: "Download for Linux (.rpm)", + url: getDownloadLink(version, ASSET_NAMES.linux.rpm(version)), + }; + } return { label: "Download for Linux (.deb)", url: getDownloadLink(version, ASSET_NAMES.linux.deb(version)), }; + } default: return { label: "View All Downloads", From 6c5ed77385e7c51a842fc35802cfa06cbadc473c Mon Sep 17 00:00:00 2001 From: AteebNoOne Date: Sun, 12 Jul 2026 17:46:51 +0500 Subject: [PATCH 6/7] fix: enforce safe architecture detection and correct rpm package format Co-authored-by: Atib Ur Rehman --- src/lib/platform.ts | 70 +++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/src/lib/platform.ts b/src/lib/platform.ts index 7fade46..ecbb462 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -45,7 +45,7 @@ const ASSET_NAMES = { linux: { appImage: (version: string) => `Teebot Flow_${version}_amd64.AppImage`, deb: (version: string) => `Teebot Flow_${version}_amd64.deb`, - rpm: (version: string) => `Teebot.Flow-${version}-1.x86_64.rpm`, + rpm: (version: string) => `teebot-flow-${version}-1.x86_64.rpm`, }, macOS: { aarch64: (version: string) => `Teebot.Flow_${version}_aarch64.dmg`, @@ -53,10 +53,34 @@ const ASSET_NAMES = { }, }; +// Cache the computed Mac architecture for the duration of the browser session +let cachedMacArch: "aarch64" | "x64" | null | undefined = undefined; + const getMacArch = (): "aarch64" | "x64" | null => { - if (typeof window === "undefined") return null; - - // 1. Try WebGL detection first (highly accurate for Apple Silicon GPUs, bypassing masked user-agent strings) + if (cachedMacArch !== undefined) { + return cachedMacArch; + } + + if (typeof window === "undefined") { + cachedMacArch = null; + return null; + } + + const userAgent = window.navigator.userAgent.toLowerCase(); + + // 1. Check for explicit, trustworthy architecture signals in the User Agent. + // We do NOT check for generic/spoofed/reduced "intel" or "mac os x" UA strings. + if (userAgent.includes("arm64") || userAgent.includes("aarch64")) { + cachedMacArch = "aarch64"; + return "aarch64"; + } + if (userAgent.includes("x86_64") || userAgent.includes("amd64") || userAgent.includes("x64")) { + cachedMacArch = "x64"; + return "x64"; + } + + // 2. Perform WebGL check to detect potential GPU/vendor masking or generic software rendering. + // We fail closed: we do NOT infer CPU architecture from generic GPU/vendor names or generic Intel strings. try { const canvas = document.createElement("canvas"); const gl = (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")) as WebGLRenderingContext | null; @@ -64,14 +88,25 @@ const getMacArch = (): "aarch64" | "x64" | null => { const debugInfo = gl.getExtension("WEBGL_debug_renderer_info"); if (debugInfo) { const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL).toLowerCase(); - if ( - renderer.includes("apple") || - renderer.includes("m1") || - renderer.includes("m2") || - renderer.includes("m3") || - renderer.includes("m4") - ) { - return "aarch64"; + + // Detect masked, generic, or software renderers + const isMaskedOrGeneric = + !renderer || + renderer.includes("swiftshader") || + renderer.includes("software") || + renderer.includes("generic") || + renderer.includes("angle") || + renderer.includes("unmasked") || + renderer.includes("stub") || + renderer.includes("null") || + renderer.includes("google") || + renderer.includes("mozilla") || + renderer.includes("webkit"); + + if (isMaskedOrGeneric) { + // Fail closed when WebGL renderer details are masked or generic + cachedMacArch = null; + return null; } } } @@ -79,15 +114,8 @@ const getMacArch = (): "aarch64" | "x64" | null => { // Proceed to fallback } - // 2. Fallback to User Agent checks - const userAgent = window.navigator.userAgent.toLowerCase(); - if (userAgent.includes("arm64") || userAgent.includes("aarch64")) { - return "aarch64"; - } - if (userAgent.includes("intel") || userAgent.includes("x64") || userAgent.includes("x86_64")) { - return "x64"; - } - + // If no explicit, trustworthy architecture signal is found, fail closed and return null + cachedMacArch = null; return null; }; From aab1a66adc2026ee465a2bc896590cf64568dc21 Mon Sep 17 00:00:00 2001 From: AteebNoOne Date: Sun, 12 Jul 2026 17:58:27 +0500 Subject: [PATCH 7/7] fix: remove ineffective WebGL check and add generic AppImage Linux fallback Co-authored-by: Atib Ur Rehman --- src/lib/platform.ts | 48 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/src/lib/platform.ts b/src/lib/platform.ts index ecbb462..a05180f 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -68,7 +68,7 @@ const getMacArch = (): "aarch64" | "x64" | null => { const userAgent = window.navigator.userAgent.toLowerCase(); - // 1. Check for explicit, trustworthy architecture signals in the User Agent. + // Check for explicit, trustworthy architecture signals in the User Agent. // We do NOT check for generic/spoofed/reduced "intel" or "mac os x" UA strings. if (userAgent.includes("arm64") || userAgent.includes("aarch64")) { cachedMacArch = "aarch64"; @@ -79,41 +79,6 @@ const getMacArch = (): "aarch64" | "x64" | null => { return "x64"; } - // 2. Perform WebGL check to detect potential GPU/vendor masking or generic software rendering. - // We fail closed: we do NOT infer CPU architecture from generic GPU/vendor names or generic Intel strings. - try { - const canvas = document.createElement("canvas"); - const gl = (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")) as WebGLRenderingContext | null; - if (gl) { - const debugInfo = gl.getExtension("WEBGL_debug_renderer_info"); - if (debugInfo) { - const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL).toLowerCase(); - - // Detect masked, generic, or software renderers - const isMaskedOrGeneric = - !renderer || - renderer.includes("swiftshader") || - renderer.includes("software") || - renderer.includes("generic") || - renderer.includes("angle") || - renderer.includes("unmasked") || - renderer.includes("stub") || - renderer.includes("null") || - renderer.includes("google") || - renderer.includes("mozilla") || - renderer.includes("webkit"); - - if (isMaskedOrGeneric) { - // Fail closed when WebGL renderer details are masked or generic - cachedMacArch = null; - return null; - } - } - } - } catch (e) { - // Proceed to fallback - } - // If no explicit, trustworthy architecture signal is found, fail closed and return null cachedMacArch = null; return null; @@ -226,9 +191,16 @@ export const getBestDownloadForOS = (os: string, version: string) => { url: getDownloadLink(version, ASSET_NAMES.linux.rpm(version)), }; } + if (distro === "debian") { + return { + label: "Download for Linux (.deb)", + url: getDownloadLink(version, ASSET_NAMES.linux.deb(version)), + }; + } + // Safe generic Linux fallback (AppImage) that does not assume a package format return { - label: "Download for Linux (.deb)", - url: getDownloadLink(version, ASSET_NAMES.linux.deb(version)), + label: "Download for Linux (AppImage)", + url: getDownloadLink(version, ASSET_NAMES.linux.appImage(version)), }; } default: