From bbf9b193d8422d4d8ab84861b3612f7130c5cde7 Mon Sep 17 00:00:00 2001 From: Rasensprenger Date: Sun, 30 Aug 2026 23:00:39 +0300 Subject: [PATCH 1/4] chore(deps): update rust crate base64 to 0.23 --- apps/tauri/src-tauri/Cargo.lock | 2 +- apps/tauri/src-tauri/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/tauri/src-tauri/Cargo.lock b/apps/tauri/src-tauri/Cargo.lock index d404d61..cdc1332 100644 --- a/apps/tauri/src-tauri/Cargo.lock +++ b/apps/tauri/src-tauri/Cargo.lock @@ -5139,7 +5139,7 @@ dependencies = [ name = "tensamin" version = "0.0.0" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "jni 0.22.4", "mtp", "reqwest", diff --git a/apps/tauri/src-tauri/Cargo.toml b/apps/tauri/src-tauri/Cargo.toml index 2a68e95..69fbe80 100644 --- a/apps/tauri/src-tauri/Cargo.toml +++ b/apps/tauri/src-tauri/Cargo.toml @@ -21,7 +21,7 @@ tauri-build = { git = "https://github.com/tauri-apps/tauri", rev = "4af26a3f7f8b tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" -base64 = "0.22" +base64 = "0.23" reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"] } tokio = { version = "1", features = ["rt-multi-thread", "sync", "time"] } mtp = { git = "https://git.methanium.net/Methanium/mtp.git", rev = "a5c8d4f0c898c78351e9d54124886c86e789a22a", features = ["client", "crypto"] } From 85921a0a0852ef59ca995057f34ab5f839325869 Mon Sep 17 00:00:00 2001 From: Alois Date: Mon, 31 Aug 2026 20:48:32 +0200 Subject: [PATCH 2/4] perf(vite): add dynamic imports --- apps/pwa/src/vite.ts | 5 + apps/web/package.json | 1 + apps/web/src/components/callPopout.tsx | 15 ++ apps/web/src/components/callRuntimeInit.tsx | 95 +++++++++ apps/web/src/components/callSidebarBox.tsx | 15 ++ apps/web/src/components/modals/profile.tsx | 2 +- apps/web/src/components/navbar.tsx | 21 +- apps/web/src/components/routeLoader.tsx | 15 ++ apps/web/src/components/sidebar.tsx | 4 +- apps/web/src/index.tsx | 194 ++---------------- apps/web/src/routes/app/layout.tsx | 2 +- apps/web/src/routes/app/shell.tsx | 78 +++++++ apps/web/vite.config.ts | 29 +++ packages/call/package.json | 1 + packages/call/src/state.ts | 93 +++++++++ packages/call/src/store.tsx | 98 +-------- packages/chat/package.json | 1 + .../chat/src/components/emoji/emojiPicker.tsx | 2 +- .../chat/src/components/emoji/emojiRanks.ts | 2 +- packages/chat/src/components/input.tsx | 64 +++--- packages/chat/src/components/media/media.tsx | 2 +- packages/chat/src/components/message.tsx | 4 +- .../src/components/messageContextMenu.tsx | 2 +- packages/chat/src/components/replyBox.tsx | 2 +- packages/markdown/src/text.tsx | 4 + packages/settings/package.json | 1 + packages/settings/src/manifest.ts | 31 +-- packages/settings/src/pages/profile.tsx | 2 +- pnpm-lock.yaml | 9 + 29 files changed, 469 insertions(+), 325 deletions(-) create mode 100644 apps/web/src/components/callPopout.tsx create mode 100644 apps/web/src/components/callRuntimeInit.tsx create mode 100644 apps/web/src/components/callSidebarBox.tsx create mode 100644 apps/web/src/components/routeLoader.tsx create mode 100644 apps/web/src/routes/app/shell.tsx create mode 100644 packages/call/src/state.ts diff --git a/apps/pwa/src/vite.ts b/apps/pwa/src/vite.ts index 962dce1..107e08f 100644 --- a/apps/pwa/src/vite.ts +++ b/apps/pwa/src/vite.ts @@ -92,6 +92,11 @@ function emitIcons(): Plugin { attrs: { name: "apple-mobile-web-app-capable", content: "yes" }, injectTo: "head", }, + { + tag: "meta", + attrs: { name: "mobile-web-app-capable", content: "yes" }, + injectTo: "head", + }, { tag: "meta", attrs: { diff --git a/apps/web/package.json b/apps/web/package.json index 9165eb8..e6b015e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -24,6 +24,7 @@ "@tensamin/chat": "workspace:*", "@tensamin/crypto": "workspace:*", "@tensamin/hotkeys": "workspace:*", + "@tensamin/markdown": "workspace:*", "@tensamin/mtp": "workspace:*", "@tensamin/notifications": "workspace:*", "@tensamin/onboarding": "workspace:*", diff --git a/apps/web/src/components/callPopout.tsx b/apps/web/src/components/callPopout.tsx new file mode 100644 index 0000000..79d5fb7 --- /dev/null +++ b/apps/web/src/components/callPopout.tsx @@ -0,0 +1,15 @@ +import { lazy, Suspense } from "react"; + +import { useCall } from "@tensamin/call/state"; + +const Popout = lazy(() => import("@tensamin/call/popout")); + +export default function CallPopout() { + const active = useCall((state) => state.state !== "closed"); + + return active ? ( + + + + ) : null; +} diff --git a/apps/web/src/components/callRuntimeInit.tsx b/apps/web/src/components/callRuntimeInit.tsx new file mode 100644 index 0000000..296057d --- /dev/null +++ b/apps/web/src/components/callRuntimeInit.tsx @@ -0,0 +1,95 @@ +import { useEffect, useState } from "react"; + +import { useTheme } from "@methanium/ui"; +import { useIsSpeaking } from "@tensamin/call/speakingState"; +import { useCall, useInitializeCall } from "@tensamin/call/store"; +import { useStorage } from "@tensamin/storage/context"; + +function createCallTrayIcon(color: string, speaking: boolean) { + const canvas = document.createElement("canvas"); + canvas.width = 32; + canvas.height = 32; + + const context = canvas.getContext("2d"); + if (!context) return undefined; + + context.globalAlpha = speaking ? 1 : 0.55; + context.fillStyle = color; + context.beginPath(); + context.arc(16, 16, 13, 0, Math.PI * 2); + context.fill(); + + if (speaking) { + context.globalAlpha = 0.3; + context.fillStyle = "#ffffff"; + context.fill(); + } + + return canvas.toDataURL("image/png"); +} + +export default function CallRuntimeInit() { + const callInvitePopup = useInitializeCall(); + const { load } = useStorage(); + const { + themeColor, + themePalette, + themePrimaryColor, + themePolarity, + themeTint, + themeCustomCss, + } = useTheme(); + const [localUserId, setLocalUserId] = useState(-1); + const [primaryColor, setPrimaryColor] = useState(""); + const inCall = useCall((state) => state.state === "open"); + const speaking = useIsSpeaking(localUserId); + + useEffect(() => { + let active = true; + + load("user_id").then((userId) => { + if (active) setLocalUserId(userId); + }); + + return () => { + active = false; + }; + }, [load]); + + useEffect(() => { + const frame = requestAnimationFrame(() => { + setPrimaryColor( + getComputedStyle(document.documentElement) + .getPropertyValue("--primary") + .trim(), + ); + }); + + return () => cancelAnimationFrame(frame); + }, [ + themeColor, + themeCustomCss, + themePalette, + themePolarity, + themePrimaryColor, + themeTint, + ]); + + useEffect(() => { + const iconDataUrl = primaryColor + ? createCallTrayIcon(primaryColor, speaking) + : undefined; + + void window.tensaminDesktop?.call + ?.setStatus?.({ + inCall, + speaking: inCall && speaking, + iconDataUrl: inCall ? iconDataUrl : undefined, + }) + .catch((error: unknown) => { + console.error("Failed to update desktop call status", error); + }); + }, [inCall, primaryColor, speaking]); + + return callInvitePopup; +} diff --git a/apps/web/src/components/callSidebarBox.tsx b/apps/web/src/components/callSidebarBox.tsx new file mode 100644 index 0000000..5480af2 --- /dev/null +++ b/apps/web/src/components/callSidebarBox.tsx @@ -0,0 +1,15 @@ +import { lazy, Suspense } from "react"; + +import { useCall } from "@tensamin/call/state"; + +const SidebarBox = lazy(() => import("@tensamin/call/sidebarBox")); + +export default function CallSidebarBox() { + const active = useCall((state) => state.state !== "closed"); + + return active ? ( + + + + ) : null; +} diff --git a/apps/web/src/components/modals/profile.tsx b/apps/web/src/components/modals/profile.tsx index 1346b19..f077783 100644 --- a/apps/web/src/components/modals/profile.tsx +++ b/apps/web/src/components/modals/profile.tsx @@ -1,6 +1,6 @@ import type { User } from "@tensamin/identity/context"; import { Avatar, AvatarFallback, AvatarImage, Button } from "@methanium/ui"; -import { Text } from "@methanium/ui/markdown"; +import Text from "@tensamin/markdown/text"; import { ChevronDown, ChevronUp } from "lucide-react"; import { useState } from "react"; diff --git a/apps/web/src/components/navbar.tsx b/apps/web/src/components/navbar.tsx index b92d30e..e8f574c 100644 --- a/apps/web/src/components/navbar.tsx +++ b/apps/web/src/components/navbar.tsx @@ -14,7 +14,7 @@ import { User, } from "lucide-react"; import { useLocation, useNavigate, useSearch } from "@tanstack/react-router"; -import { joinCall, useCall } from "@tensamin/call/store"; +import { useCall } from "@tensamin/call/state"; import Wrapper from "@tensamin/identity/wrapper"; import { Skeleton } from "@methanium/ui"; import { @@ -140,7 +140,9 @@ export default function Navbar({ forMobile }: { forMobile: boolean }) {