From 4709773ddca3896d7e983e94159434471f8ffeb3 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sat, 17 Jan 2026 12:29:28 -0500 Subject: [PATCH 01/11] More options for positioning/anchor, opacity, name max length --- apps/desktop/src/App.tsx | 10 +- apps/desktop/src/components/nav-bar.tsx | 12 +- apps/desktop/src/components/user.tsx | 38 +++-- apps/desktop/src/config.ts | 19 ++- apps/desktop/src/hooks/use-align.ts | 32 ++++- apps/desktop/src/views/channel.tsx | 20 ++- .../src/views/settings/configuration.tsx | 130 +++++++++++++++--- 7 files changed, 217 insertions(+), 44 deletions(-) diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index d1862215..4be3bb43 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -34,7 +34,7 @@ function App() { return (
@@ -46,10 +46,10 @@ function App() { /> - - } /> - } /> - + + } /> + } /> +
); diff --git a/apps/desktop/src/components/nav-bar.tsx b/apps/desktop/src/components/nav-bar.tsx index 48f7eb05..aa19b3d2 100644 --- a/apps/desktop/src/components/nav-bar.tsx +++ b/apps/desktop/src/components/nav-bar.tsx @@ -16,6 +16,7 @@ import { useState } from "react"; import { CHANNEL_TYPES } from "@/constants"; import { Metric, track } from "@/metrics"; import { invoke } from "@tauri-apps/api/core"; +import { emit } from "@tauri-apps/api/event"; const mapping = { left: 0, center: 1, @@ -64,6 +65,11 @@ export const NavBar = ({ const [channelName, setChannelName] = useState(); const [currentAlignment, setCurrentAlignment] = useState(mapping[alignDirection]); + // keep local currentAlignment in sync when the prop changes (e.g., loaded from config) + useEffect(() => { + setCurrentAlignment(mapping[alignDirection] ?? mapping.right); + }, [alignDirection]); + const opacity = pin && location.pathname === "/channel" ? "opacity-0" : "opacity-100"; const IconComponent = horizontalAlignments[currentAlignment]?.icon || ArrowLeftToLine; const showUpdateButton = location.pathname !== "/settings" && isUpdateAvailable; @@ -122,8 +128,10 @@ export const NavBar = ({ onClick={async () => { const newAlignment = (currentAlignment + 1) % horizontalAlignments.length; setCurrentAlignment(newAlignment); - setAlignDirection(horizontalAlignments[newAlignment]?.direction || "center"); - await Config.set("horizontal", horizontalAlignments[newAlignment]?.direction || "center"); + const dir = horizontalAlignments[newAlignment]?.direction || "center"; + setAlignDirection(dir); + await Config.set("horizontal", dir); + await emit("config_update", await Config.getConfig()); }} /> diff --git a/apps/desktop/src/components/user.tsx b/apps/desktop/src/components/user.tsx index b4a0ad9a..9d3b3a96 100644 --- a/apps/desktop/src/components/user.tsx +++ b/apps/desktop/src/components/user.tsx @@ -1,16 +1,20 @@ -import type { DirectionLR } from "@/config"; +import type { DirectionLR, OpacityTarget } from "@/config"; +import { useConfigValue } from "@/hooks/use-config-value"; import type { OverlayedUser } from "../types"; import { HeadphonesOff } from "./icons/headphones-off"; import { MicOff } from "./icons/mic-off"; +import { cn } from "@/utils/tw"; export const User = ({ item, alignDirection, opacity, + opacityTarget, }: { item: OverlayedUser; alignDirection: DirectionLR; opacity: number; + opacityTarget: OpacityTarget; }) => { const { id, selfMuted, selfDeafened, talking, muted, deafened, avatarHash } = item; @@ -20,7 +24,13 @@ export const User = ({ const mutedClass = selfMuted || muted ? "text-zinc-400" : ""; const opacityStyle = talking ? "100%" : `${opacity}%`; - // TODO: use tw merge so this looks better i guess + const { value: maxUsernameLength } = useConfigValue("maxUsernameLength"); + + const displayName = (() => { + const name = item.username ?? ""; + if (!maxUsernameLength || name.length <= maxUsernameLength) return name; + return name.slice(0, Math.max(0, maxUsernameLength)) + "…"; + })(); function renderCheeseMicIcon() { let icon = null; @@ -45,9 +55,11 @@ export const User = ({ return (
{icon}
@@ -56,10 +68,11 @@ export const User = ({ return (
- {item.username} + {displayName}
{(selfMuted || muted) && } {(selfDeafened || deafened) && } diff --git a/apps/desktop/src/config.ts b/apps/desktop/src/config.ts index 8bfb5962..183dcfad 100644 --- a/apps/desktop/src/config.ts +++ b/apps/desktop/src/config.ts @@ -5,6 +5,8 @@ import * as Sentry from "@sentry/react"; export type DirectionLR = "left" | "right" | "center"; export type DirectionTB = "top" | "bottom"; +export type OpacityTarget = "all" | "username-box"; + // TODO: this is hard to use we zzz // NOTE: how can i handle versions updates where i add new keys // NOTE: this looks cool https://github.com/harshkhandeparkar/tauri-settings/issues @@ -17,6 +19,8 @@ export interface OverlayedConfig { showOnlyTalkingUsers: boolean; showOwnUser: boolean; opacity: number; + opacityTarget: OpacityTarget; + maxUsernameLength: number; } export type OverlayedConfigKey = keyof OverlayedConfig; @@ -31,6 +35,8 @@ export const DEFAULT_OVERLAYED_CONFIG: OverlayedConfig = { showOnlyTalkingUsers: false, showOwnUser: true, opacity: 100, + opacityTarget: "all", + maxUsernameLength: 40, }; const CONFIG_FILE_NAME = "config.json"; @@ -94,10 +100,19 @@ export class Config { } async set(key: K, value: OverlayedConfig[K]): Promise { - await this.load(); + // Always re-load the on-disk config to avoid races where multiple + // concurrent callers might overwrite recent changes. This ensures we + // merge against the latest file contents before writing. + try { + const config = await readTextFile(CONFIG_FILE_NAME, { baseDir: BaseDirectory.AppConfig }); + this.config = JSON.parse(config); + } catch (e: unknown) { + // ignore and fall back to current in-memory or defaults + this.config = { ...DEFAULT_OVERLAYED_CONFIG, ...this.config }; + } this.config[key] = value; - this.save(); + await this.save(); } async save() { diff --git a/apps/desktop/src/hooks/use-align.ts b/apps/desktop/src/hooks/use-align.ts index e3a623f6..7be9d318 100644 --- a/apps/desktop/src/hooks/use-align.ts +++ b/apps/desktop/src/hooks/use-align.ts @@ -1,10 +1,36 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { type DirectionLR, type DirectionTB } from "../config"; +import Config, { DEFAULT_OVERLAYED_CONFIG, type OverlayedConfig } from "@/config"; +import { listen } from "@tauri-apps/api/event"; // TODO: make it update automatically when the window moves export const useAlign = () => { - const [horizontal, setHorizontalDirection] = useState("right"); - const [vertical, setVerticalDirection] = useState("bottom"); + const [horizontal, setHorizontalDirection] = useState(DEFAULT_OVERLAYED_CONFIG.horizontal); + const [vertical, setVerticalDirection] = useState(DEFAULT_OVERLAYED_CONFIG.vertical); + + useEffect(() => { + const init = async () => { + const saved = await Config.get("horizontal"); + setHorizontalDirection(saved as DirectionLR); + + const savedV = await Config.get("vertical"); + setVerticalDirection(savedV as DirectionTB); + }; + + init(); + + const listenFn = listen("config_update", event => { + const { payload } = event; + if (payload.horizontal) setHorizontalDirection(payload.horizontal as DirectionLR); + if (payload.vertical) setVerticalDirection(payload.vertical as DirectionTB); + }); + + return () => { + (async () => { + await listenFn; + })(); + }; + }, []); return { horizontal, vertical, setHorizontalDirection, setVerticalDirection }; }; diff --git a/apps/desktop/src/views/channel.tsx b/apps/desktop/src/views/channel.tsx index 9da40dd0..7d5301bf 100644 --- a/apps/desktop/src/views/channel.tsx +++ b/apps/desktop/src/views/channel.tsx @@ -1,5 +1,6 @@ import type { DirectionLR } from "@/config"; import { User } from "../components/user"; +import { cn } from "@/utils/tw"; import { useAppStore } from "../store"; import { useConfigValue } from "@/hooks/use-config-value"; @@ -9,6 +10,8 @@ export const ChannelView = ({ alignDirection }: { alignDirection: DirectionLR }) const { value: showOnlyTalkingUsers } = useConfigValue("showOnlyTalkingUsers"); const { value: showOwnUser } = useConfigValue("showOwnUser"); const { value: opacity } = useConfigValue("opacity"); + const { value: opacityTarget } = useConfigValue("opacityTarget"); + const { value: vertical } = useConfigValue("vertical"); const allUsers = Object.entries(users); let userList = showOnlyTalkingUsers ? allUsers.filter(([, item]) => item.talking) : allUsers; @@ -21,10 +24,21 @@ export const ChannelView = ({ alignDirection }: { alignDirection: DirectionLR }) }); return ( -
-
+
+
{userList.map(([, item]) => ( - + ))}
diff --git a/apps/desktop/src/views/settings/configuration.tsx b/apps/desktop/src/views/settings/configuration.tsx index 7da168ff..e0eccd85 100644 --- a/apps/desktop/src/views/settings/configuration.tsx +++ b/apps/desktop/src/views/settings/configuration.tsx @@ -1,4 +1,4 @@ -import { Input } from "@/components/ui/input"; +import { Slider } from "@/components/ui/slider"; import { Switch } from "@/components/ui/switch"; import Config from "@/config"; import { useConfigValue } from "@/hooks/use-config-value"; @@ -7,13 +7,17 @@ import { emit } from "@tauri-apps/api/event"; export const Configuration = () => { const { value: showOnlyTalkingUsers } = useConfigValue("showOnlyTalkingUsers"); const { value: opacity } = useConfigValue("opacity"); + const { value: opacityTarget } = useConfigValue("opacityTarget"); + const { value: vertical } = useConfigValue("vertical"); + const { value: horizontal } = useConfigValue("horizontal"); + const { value: maxUsernameLength } = useConfigValue("maxUsernameLength"); return (
-
+
@@ -28,27 +32,117 @@ export const Configuration = () => { }} />
-
-
+
+ + +
+
+ - { - const newOpacity = event.target.value; - await Config.set("opacity", Number(newOpacity)); + const newTarget = event.target.value as "top" | "bottom"; + await Config.set("vertical", newTarget); await emit("config_update", await Config.getConfig()); }} - className="w-20" - /> + className="w-40 p-1 rounded border bg-zinc-800 text-white outline-none focus:ring-0" + > + + + +
+
+ + +
+
+ +
+
+ { + const newVal = val[0] ?? opacity; + await Config.set("opacity", Number(newVal)); + await emit("config_update", await Config.getConfig()); + }} + /> +
+
+ {opacity} % +
+
); From f62e1c69b7d61e7a5ac6d592799a7af8c1edc2d9 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sat, 17 Jan 2026 13:21:02 -0500 Subject: [PATCH 02/11] Scaling --- apps/desktop/src/components/user.tsx | 11 ++++++++- apps/desktop/src/config.ts | 2 ++ apps/desktop/src/views/channel.tsx | 14 +++++++++-- .../src/views/settings/configuration.tsx | 24 +++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/components/user.tsx b/apps/desktop/src/components/user.tsx index 9d3b3a96..cafa65e7 100644 --- a/apps/desktop/src/components/user.tsx +++ b/apps/desktop/src/components/user.tsx @@ -10,11 +10,13 @@ export const User = ({ alignDirection, opacity, opacityTarget, + userScale, }: { item: OverlayedUser; alignDirection: DirectionLR; opacity: number; opacityTarget: OpacityTarget; + userScale: number; }) => { const { id, selfMuted, selfDeafened, talking, muted, deafened, avatarHash } = item; @@ -23,6 +25,8 @@ export const User = ({ const talkingClass = talking ? "border-green-500" : "border-zinc-800"; const mutedClass = selfMuted || muted ? "text-zinc-400" : ""; const opacityStyle = talking ? "100%" : `${opacity}%`; + const scaleFactor = (userScale ?? 100) / 100; + const transformOrigin = alignDirection === "left" ? "left center" : alignDirection === "right" ? "right center" : "center center"; const { value: maxUsernameLength } = useConfigValue("maxUsernameLength"); @@ -72,7 +76,12 @@ export const User = ({ "flex gap-2 py-1 p-2 justify-start items-center transition-opacity", alignDirection == "right" ? "flex-row-reverse" : "flex-row" )} - style={opacityTarget === "all" ? { opacity: opacityStyle } : undefined} + style={{ + ...(opacityTarget === "all" ? { opacity: opacityStyle } : {}), + transform: scaleFactor !== 1 ? `scale(${scaleFactor})` : undefined, + transformOrigin: scaleFactor !== 1 ? transformOrigin : undefined, + willChange: scaleFactor !== 1 ? "transform" : undefined, + }} >
{userList.map(([, item]) => ( - + ))}
diff --git a/apps/desktop/src/views/settings/configuration.tsx b/apps/desktop/src/views/settings/configuration.tsx index e0eccd85..2eb365eb 100644 --- a/apps/desktop/src/views/settings/configuration.tsx +++ b/apps/desktop/src/views/settings/configuration.tsx @@ -11,6 +11,7 @@ export const Configuration = () => { const { value: vertical } = useConfigValue("vertical"); const { value: horizontal } = useConfigValue("horizontal"); const { value: maxUsernameLength } = useConfigValue("maxUsernameLength"); + const { value: userScale } = useConfigValue("userScale"); return (
@@ -144,6 +145,29 @@ export const Configuration = () => {
+
+ +
+
+ { + const newVal = val[0] ?? userScale; + await Config.set("userScale", Number(newVal)); + await emit("config_update", await Config.getConfig()); + }} + /> +
+
+ {userScale} % +
+
+
); }; From f4ad292f38a8706d8fe813ed5bfc1032597dda25 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sat, 17 Jan 2026 14:59:38 -0500 Subject: [PATCH 03/11] Improves settings window and pinning behavior Ensures the settings window starts minimized and unminimizes when opened. Modifies the pin toggle to target the main overlay window consistently. Broadcasts pin changes to all webviews for consistent state. Persists main window size and position on resize/move events. Adds a pin toggle to the settings page. --- apps/desktop/src-tauri/src/commands.rs | 24 ++++++++-- apps/desktop/src-tauri/src/main.rs | 49 ++++++++++++++------- apps/desktop/src-tauri/tauri.conf.json | 2 +- apps/desktop/src/App.tsx | 7 ++- apps/desktop/src/components/nav-bar.tsx | 7 +-- apps/desktop/src/views/settings/account.tsx | 24 +++++++++- 6 files changed, 86 insertions(+), 27 deletions(-) diff --git a/apps/desktop/src-tauri/src/commands.rs b/apps/desktop/src-tauri/src/commands.rs index 5cdb1cbf..41d8a12f 100644 --- a/apps/desktop/src-tauri/src/commands.rs +++ b/apps/desktop/src-tauri/src/commands.rs @@ -12,6 +12,7 @@ pub fn open_settings(window: WebviewWindow, update: bool) { let app = window.app_handle(); let settings_windows = app.get_webview_window(SETTINGS_WINDOW_NAME); if let Some(settings_windows) = settings_windows { + let _ = settings_windows.unminimize(); settings_windows.show(); settings_windows.set_focus(); if update { @@ -47,12 +48,18 @@ pub fn toggle_pin(window: WebviewWindow, pin: State, menu: State()); - _set_pin(value, &window, pin, menu); + // Always target the main overlay window so pinning from other windows + // (e.g., settings) does not affect those windows. + if let Some(main_win) = app.get_webview_window(MAIN_WINDOW_NAME) { + _set_pin(value, &main_win, pin, menu); + } } #[tauri::command] pub fn set_pin(window: WebviewWindow, pin: State, menu: State, value: bool) { - _set_pin(value, &window, pin, menu); + if let Some(main_win) = window.app_handle().get_webview_window(MAIN_WINDOW_NAME) { + _set_pin(value, &main_win, pin, menu); + } } impl Deref for Pinned { @@ -75,9 +82,20 @@ fn _set_pin(value: bool, window: &WebviewWindow, pinned: State, menu: St // @d0nutptr cooked here pinned.store(value, std::sync::atomic::Ordering::Relaxed); - // let the client know + // emit to the main window and also broadcast to all webviews so other + // windows (like settings) can react to the change. window.emit(TRAY_TOGGLE_PIN, value).unwrap(); + // Broadcast the pin change to all webviews so other + // windows (like settings) can react to the change. + let app_handle = window.app_handle(); + if let Some(main_win) = app_handle.get_webview_window(MAIN_WINDOW_NAME) { + let _ = main_win.emit(TRAY_TOGGLE_PIN, value); + } + if let Some(settings_win) = app_handle.get_webview_window(SETTINGS_WINDOW_NAME) { + let _ = settings_win.emit(TRAY_TOGGLE_PIN, value); + } + // invert the label for the tray if let Some(toggle_pin_menu_item) = menu.lock().ok().and_then(|m| m.get(TRAY_TOGGLE_PIN)) { let enable_or_disable = if value { "Unpin" } else { "Pin" }; diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs index 8f844c23..d4685e28 100644 --- a/apps/desktop/src-tauri/src/main.rs +++ b/apps/desktop/src-tauri/src/main.rs @@ -152,6 +152,9 @@ fn main() { width: SETTINGS_WINDOW_WIDTH, height: SETTINGS_WINDOW_HEIGHT, }); + // Start the settings window minimized so it's available but not intrusive + // The `show` call from the UI will restore/unminimize it. + settings.minimize().ok(); Ok(()) }) @@ -168,22 +171,36 @@ fn main() { .build(tauri::generate_context!()) .expect("An error occured while running the app!") .run(|app, event| { - if let tauri::RunEvent::WindowEvent { - event: tauri::WindowEvent::CloseRequested { api, .. }, - label, - .. - } = event - { - if label == SETTINGS_WINDOW_NAME { - let win = app.get_webview_window(label.as_str()).unwrap(); - win.hide().unwrap(); - } - - if label == MAIN_WINDOW_NAME { - app.save_window_state(StateFlags::POSITION | StateFlags::SIZE); - std::process::exit(0); - } else { - api.prevent_close(); + use tauri::WindowEvent as WEvent; + + if let tauri::RunEvent::WindowEvent { event: we, label, .. } = event { + match we { + WEvent::CloseRequested { api, .. } => { + if label == SETTINGS_WINDOW_NAME { + let win = app.get_webview_window(label.as_str()).unwrap(); + // Minimize the settings window instead of hiding/closing it when + // the user clicks the X so it remains available to restore. + win.minimize().ok(); + } + + if label == MAIN_WINDOW_NAME { + // Save state on close + app.save_window_state(StateFlags::POSITION | StateFlags::SIZE); + std::process::exit(0); + } else { + api.prevent_close(); + } + } + + WEvent::Resized(_) | WEvent::Moved(_) => { + if label == MAIN_WINDOW_NAME { + // Also save state whenever the main window is moved or resized so + // we persist the latest bounds even if the process is terminated + app.save_window_state(StateFlags::POSITION | StateFlags::SIZE); + } + } + + _ => {} } } }); diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index d1a5fe66..ca70b57f 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -52,7 +52,7 @@ "transparent": true, "decorations": true, "resizable": false, - "visible": false, + "visible": true, "label": "settings", "url": "#settings", "title": "Overlayed - Settings", diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 4be3bb43..93346fd4 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -1,4 +1,4 @@ -import { Routes, Route } from "react-router-dom"; +import { Routes, Route, useLocation } from "react-router-dom"; import { MainView } from "./views/main"; import { ChannelView } from "./views/channel"; @@ -30,12 +30,15 @@ function App() { const { pin } = usePin(); const { horizontal, setHorizontalDirection } = useAlign(); const visibleClass = visible ? "opacity-100" : "opacity-0"; + const location = useLocation(); + const isSettingsWindow = location.pathname === "/settings"; return (
{!canary && showUpdateButton && ( - )} - -
+
+ +
{ @@ -167,7 +187,7 @@ export const Account = () => { open={showLogoutDialog} > - @@ -208,7 +228,7 @@ export const Account = () => { open={showQuitDialog} > - From 95f69d4143541b3b44820eb9242670963ac57976 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sat, 17 Jan 2026 15:11:36 -0500 Subject: [PATCH 04/11] Cursor adjustments --- apps/desktop/src/components/ui/slider.tsx | 2 +- apps/desktop/src/components/ui/tabs.tsx | 6 +++++- apps/desktop/src/views/settings/configuration.tsx | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/components/ui/slider.tsx b/apps/desktop/src/components/ui/slider.tsx index 32eec71f..693e7298 100644 --- a/apps/desktop/src/components/ui/slider.tsx +++ b/apps/desktop/src/components/ui/slider.tsx @@ -15,7 +15,7 @@ const Slider = React.forwardRef< - + )); Slider.displayName = SliderPrimitive.Root.displayName; diff --git a/apps/desktop/src/components/ui/tabs.tsx b/apps/desktop/src/components/ui/tabs.tsx index a9addafa..7ff69ca7 100644 --- a/apps/desktop/src/components/ui/tabs.tsx +++ b/apps/desktop/src/components/ui/tabs.tsx @@ -27,7 +27,11 @@ const TabsTrigger = React.forwardRef< { await emit("config_update", await Config.getConfig()); }} - className="w-40 p-1 rounded border bg-zinc-800 text-white outline-none focus:ring-0" + className="w-40 p-1 rounded border bg-zinc-800 text-white outline-none focus:ring-0 cursor-pointer" > @@ -89,7 +89,7 @@ export const Configuration = () => { await emit("config_update", await Config.getConfig()); }} - className="w-40 p-1 rounded border bg-zinc-800 text-white outline-none focus:ring-0" + className="w-40 p-1 rounded border bg-zinc-800 text-white outline-none focus:ring-0 cursor-pointer" >
); diff --git a/apps/desktop/src/components/nav-bar.tsx b/apps/desktop/src/components/nav-bar.tsx index 50c1f09f..63a1282b 100644 --- a/apps/desktop/src/components/nav-bar.tsx +++ b/apps/desktop/src/components/nav-bar.tsx @@ -93,7 +93,7 @@ export const NavBar = ({
logo )} - - +
+ + {showFtue && ( +
+ {/* Arrow pointing up toward the pin button */} +
+
+

+ {getTrayHint(os)} +

+ +
+
+ )} +
+ + {import.meta.env.DEV && ( + + )}
@@ -257,8 +278,8 @@ export const Account = () => { -
+
From 53ab9644efb259d3ccd3460979c338fc41456e6b Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sun, 19 Apr 2026 14:46:32 -0400 Subject: [PATCH 07/11] Option to hide taskbar when pinned --- apps/desktop/src-tauri/src/commands.rs | 82 +++++++++++++++++-- apps/desktop/src-tauri/src/constants.rs | 2 +- apps/desktop/src-tauri/src/main.rs | 4 + apps/desktop/src-tauri/src/tray.rs | 9 +- apps/desktop/src-tauri/tauri.conf.json | 2 +- apps/desktop/src/App.tsx | 11 +++ apps/desktop/src/config.ts | 2 + .../src/views/settings/configuration.tsx | 24 ++++++ 8 files changed, 126 insertions(+), 10 deletions(-) diff --git a/apps/desktop/src-tauri/src/commands.rs b/apps/desktop/src-tauri/src/commands.rs index 5222670f..00bed9c0 100644 --- a/apps/desktop/src-tauri/src/commands.rs +++ b/apps/desktop/src-tauri/src/commands.rs @@ -5,7 +5,7 @@ use std::{ use tauri::{image::Image, menu::Menu, AppHandle, Emitter, Manager, State, WebviewWindow, Wry}; -use crate::{constants::*, Pinned, TrayMenu}; +use crate::{constants::*, HideTaskbarWhenPinned, Pinned, TrayMenu}; #[tauri::command] pub fn open_settings(window: WebviewWindow, update: bool) { @@ -51,21 +51,32 @@ pub fn open_overlay_devtools(window: WebviewWindow) { } #[tauri::command] -pub fn toggle_pin(window: WebviewWindow, pin: State, menu: State) { +pub fn toggle_pin( + window: WebviewWindow, + pin: State, + menu: State, + hide_taskbar: State, +) { let app = window.app_handle(); let value = !get_pin(app.state::()); // Always target the main overlay window so pinning from other windows // (e.g., settings) does not affect those windows. if let Some(main_win) = app.get_webview_window(MAIN_WINDOW_NAME) { - _set_pin(value, &main_win, pin, menu); + _set_pin(value, &main_win, pin, menu, hide_taskbar); } } #[tauri::command] -pub fn set_pin(window: WebviewWindow, pin: State, menu: State, value: bool) { +pub fn set_pin( + window: WebviewWindow, + pin: State, + menu: State, + hide_taskbar: State, + value: bool, +) { if let Some(main_win) = window.app_handle().get_webview_window(MAIN_WINDOW_NAME) { - _set_pin(value, &main_win, pin, menu); + _set_pin(value, &main_win, pin, menu, hide_taskbar); } } @@ -85,7 +96,41 @@ impl Deref for TrayMenu { } } -fn _set_pin(value: bool, window: &WebviewWindow, pinned: State, menu: State) { +impl Deref for HideTaskbarWhenPinned { + type Target = AtomicBool; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn apply_taskbar_visibility(app: &AppHandle, pinned: bool, hide_taskbar_when_pinned: bool) { + let skip_taskbar = pinned && hide_taskbar_when_pinned; + + if let Some(main_window) = app.get_webview_window(MAIN_WINDOW_NAME) { + #[cfg(target_os = "windows")] + main_window.set_skip_taskbar(skip_taskbar).ok(); + } + + #[cfg(target_os = "macos")] + { + use tauri::ActivationPolicy; + app.set_activation_policy(if skip_taskbar { + ActivationPolicy::Accessory + } else { + ActivationPolicy::Regular + }) + .ok(); + } +} + +fn _set_pin( + value: bool, + window: &WebviewWindow, + pinned: State, + menu: State, + hide_taskbar: State, +) { // @d0nutptr cooked here pinned.store(value, std::sync::atomic::Ordering::Relaxed); @@ -130,6 +175,12 @@ fn _set_pin(value: bool, window: &WebviewWindow, pinned: State, menu: St window.set_ignore_cursor_events(value); + apply_taskbar_visibility( + &window.app_handle(), + value, + hide_taskbar.load(std::sync::atomic::Ordering::Relaxed), + ); + // update the tray icon update_tray_icon(window.app_handle(), value); } @@ -147,3 +198,22 @@ pub fn update_tray_icon(app: &AppHandle, pinned: bool) { } } } + +#[tauri::command] +pub fn set_hide_taskbar_when_pinned( + window: WebviewWindow, + hide_taskbar_when_pinned: bool, + pinned: State, + hide_taskbar: State, +) { + hide_taskbar.store( + hide_taskbar_when_pinned, + std::sync::atomic::Ordering::Relaxed, + ); + + apply_taskbar_visibility( + &window.app_handle(), + pinned.load(std::sync::atomic::Ordering::Relaxed), + hide_taskbar_when_pinned, + ); +} diff --git a/apps/desktop/src-tauri/src/constants.rs b/apps/desktop/src-tauri/src/constants.rs index b125043b..ac1b5827 100644 --- a/apps/desktop/src-tauri/src/constants.rs +++ b/apps/desktop/src-tauri/src/constants.rs @@ -25,4 +25,4 @@ pub static OVERLAYED_NORMAL_LEVEL: i32 = 8; /// HACK: this allows constraint of the size of the settings window /// because the save sate plugin will not allow use to filter windows out pub const SETTINGS_WINDOW_WIDTH: i32 = 600; -pub const SETTINGS_WINDOW_HEIGHT: i32 = 400; +pub const SETTINGS_WINDOW_HEIGHT: i32 = 480; diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs index 5823e655..d2afb88e 100644 --- a/apps/desktop/src-tauri/src/main.rs +++ b/apps/desktop/src-tauri/src/main.rs @@ -40,6 +40,8 @@ use tauri::WebviewWindow; pub struct Pinned(AtomicBool); +pub struct HideTaskbarWhenPinned(AtomicBool); + pub struct TrayMenu(Mutex>); #[cfg(target_os = "macos")] @@ -115,6 +117,7 @@ fn main() { app = app .manage(Pinned(AtomicBool::new(false))) + .manage(HideTaskbarWhenPinned(AtomicBool::new(false))) .setup(move |app| { debug!("starting app..."); let window = app.get_webview_window(MAIN_WINDOW_NAME).unwrap(); @@ -167,6 +170,7 @@ fn main() { open_overlay_devtools, close_settings, open_settings, + set_hide_taskbar_when_pinned, ]); app diff --git a/apps/desktop/src-tauri/src/tray.rs b/apps/desktop/src-tauri/src/tray.rs index cb775ed8..ef93d069 100644 --- a/apps/desktop/src-tauri/src/tray.rs +++ b/apps/desktop/src-tauri/src/tray.rs @@ -10,7 +10,7 @@ use anyhow::Result; use tauri_plugin_window_state::{AppHandleExt, StateFlags}; use crate::{ - commands, toggle_pin, Pinned, TrayMenu, MAIN_WINDOW_NAME, OVERLAYED, SETTINGS_WINDOW_NAME, + commands, toggle_pin, HideTaskbarWhenPinned, Pinned, TrayMenu, MAIN_WINDOW_NAME, OVERLAYED, SETTINGS_WINDOW_NAME, TRAY_OPEN_DEVTOOLS_MAIN, TRAY_OPEN_DEVTOOLS_SETTINGS, TRAY_QUIT, TRAY_RELOAD, TRAY_SETTINGS, TRAY_SHOW_APP, TRAY_TOGGLE_PIN, }; @@ -52,7 +52,12 @@ impl Tray { TRAY_TOGGLE_PIN => { let window = app.get_webview_window(MAIN_WINDOW_NAME).unwrap(); - toggle_pin(window, app.state::(), app.state::()) + toggle_pin( + window, + app.state::(), + app.state::(), + app.state::(), + ) } TRAY_SHOW_APP => { let window = app.get_webview_window(MAIN_WINDOW_NAME).unwrap(); diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index d1a5fe66..073a3dd5 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -57,7 +57,7 @@ "url": "#settings", "title": "Overlayed - Settings", "width": 600, - "height": 400 + "height": 480 } ], "withGlobalTauri": true, diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 2097ab17..5713cb1c 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -14,6 +14,8 @@ import { Toaster } from "./components/ui/toaster"; import { useEffect } from "react"; import { useSocket } from "./rpc/manager"; import { cn } from "./utils/tw"; +import Config from "./config"; +import { invoke } from "@tauri-apps/api/core"; function App() { useDisableWebFeatures(); @@ -24,6 +26,15 @@ function App() { console.log(`%cOverlayed ${window.location.hash} Window`, styleForLog); }, []); + useEffect(() => { + (async () => { + const config = await Config.getConfig(); + await invoke("set_hide_taskbar_when_pinned", { + hideTaskbarWhenPinned: config.hideTaskbarWhenPinned, + }); + })(); + }, []); + const { update } = useUpdate(); const { visible } = useAppStore(); diff --git a/apps/desktop/src/config.ts b/apps/desktop/src/config.ts index bab28f3b..25bffe96 100644 --- a/apps/desktop/src/config.ts +++ b/apps/desktop/src/config.ts @@ -22,6 +22,7 @@ export interface OverlayedConfig { opacityTarget: OpacityTarget; maxUsernameLength: number; userScale: number; + hideTaskbarWhenPinned: boolean; } export type OverlayedConfigKey = keyof OverlayedConfig; @@ -39,6 +40,7 @@ export const DEFAULT_OVERLAYED_CONFIG: OverlayedConfig = { opacityTarget: "all", maxUsernameLength: 40, userScale: 100, + hideTaskbarWhenPinned: false, }; const CONFIG_FILE_NAME = "config.json"; diff --git a/apps/desktop/src/views/settings/configuration.tsx b/apps/desktop/src/views/settings/configuration.tsx index 0e913e5b..bebe410e 100644 --- a/apps/desktop/src/views/settings/configuration.tsx +++ b/apps/desktop/src/views/settings/configuration.tsx @@ -3,6 +3,7 @@ import { Switch } from "@/components/ui/switch"; import Config from "@/config"; import { useConfigValue } from "@/hooks/use-config-value"; import { emit } from "@tauri-apps/api/event"; +import { invoke } from "@tauri-apps/api/core"; export const Configuration = () => { const { value: showOnlyTalkingUsers } = useConfigValue("showOnlyTalkingUsers"); @@ -12,6 +13,7 @@ export const Configuration = () => { const { value: horizontal } = useConfigValue("horizontal"); const { value: maxUsernameLength } = useConfigValue("maxUsernameLength"); const { value: userScale } = useConfigValue("userScale"); + const { value: hideTaskbarWhenPinned } = useConfigValue("hideTaskbarWhenPinned"); return (
@@ -192,6 +194,28 @@ export const Configuration = () => {
+
+ + { + const newBool = !hideTaskbarWhenPinned; + await Config.set("hideTaskbarWhenPinned", newBool); + + await invoke("set_hide_taskbar_when_pinned", { + hideTaskbarWhenPinned: newBool, + }); + + await emit("config_update", await Config.getConfig()); + }} + /> +
); }; From c109d9a0e6efb672cb641c56bb5b89bda0a91acf Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sun, 19 Apr 2026 15:10:34 -0400 Subject: [PATCH 08/11] Allow building without updater unsigning --- LOCAL.md | 5 +++++ apps/desktop/package.json | 1 + apps/desktop/src-tauri/tauri.conf.unsigned.json | 5 +++++ apps/desktop/turbo.json | 1 + package.json | 1 + 5 files changed, 13 insertions(+) create mode 100644 apps/desktop/src-tauri/tauri.conf.unsigned.json diff --git a/LOCAL.md b/LOCAL.md index 5da4a22d..6f813a48 100644 --- a/LOCAL.md +++ b/LOCAL.md @@ -48,6 +48,11 @@ pnpm start:mocked pnpm build:desktop ``` +### 4b. Building locally without updater signing +``` +pnpm build:desktop:unsigned +``` + ### 5. Building the canary version locally ``` pnpm build:canary diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 6c716ae8..11704d23 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -9,6 +9,7 @@ "dev": "vite", "build": "tsc && vite build", "build:canary": "tauri build --config \"src-tauri/tauri.conf.canary.json\"", + "build:desktop:unsigned": "tauri build --config \"src-tauri/tauri.conf.unsigned.json\"", "generate:commit-hash": "node scripts/generate-commit-hash.js", "lint": "eslint", "clean": "rm -rf dist .turbo node_modules src-tauri/target", diff --git a/apps/desktop/src-tauri/tauri.conf.unsigned.json b/apps/desktop/src-tauri/tauri.conf.unsigned.json new file mode 100644 index 00000000..55a99462 --- /dev/null +++ b/apps/desktop/src-tauri/tauri.conf.unsigned.json @@ -0,0 +1,5 @@ +{ + "bundle": { + "createUpdaterArtifacts": false + } +} diff --git a/apps/desktop/turbo.json b/apps/desktop/turbo.json index d5fc829d..2d4eedc3 100644 --- a/apps/desktop/turbo.json +++ b/apps/desktop/turbo.json @@ -8,6 +8,7 @@ "outputs": ["dist/**"] }, "build:desktop": {}, + "build:desktop:unsigned": {}, "start:canary": {}, "build:canary": {}, "start": { diff --git a/package.json b/package.json index 40ddb36b..e5acaf07 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "desktop": "pnpm turbo run start --filter=desktop", "build": "turbo run build", "build:desktop": "turbo run build:desktop", + "build:desktop:unsigned": "turbo run build:desktop:unsigned", "build:canary": "turbo run build:canary", "check:format": "turbo run check:format", "clean": "turbo run clean && rm -rf node_modules .turbo", From 790ef6d5e98de5aa4c7d1d41381a8983e3cf4531 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sun, 19 Apr 2026 15:11:16 -0400 Subject: [PATCH 09/11] Hide FTUE on auth screen --- apps/desktop/src/components/nav-bar.tsx | 4 ++-- apps/desktop/src/rpc/manager.ts | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/components/nav-bar.tsx b/apps/desktop/src/components/nav-bar.tsx index c0c82452..861c149f 100644 --- a/apps/desktop/src/components/nav-bar.tsx +++ b/apps/desktop/src/components/nav-bar.tsx @@ -62,7 +62,7 @@ export const NavBar = ({ }) => { const location = useLocation(); const navigate = useNavigate(); - const { currentChannel } = useAppStore(); + const { currentChannel, me } = useAppStore(); const [channelName, setChannelName] = useState(); const [currentAlignment, setCurrentAlignment] = useState(mapping[alignDirection]); @@ -206,7 +206,7 @@ export const NavBar = ({ }} /> - {showFtue && ( + {showFtue && !!me && (
{/* Arrow pointing up toward the pin button */}
diff --git a/apps/desktop/src/rpc/manager.ts b/apps/desktop/src/rpc/manager.ts index f961e43d..474b542c 100644 --- a/apps/desktop/src/rpc/manager.ts +++ b/apps/desktop/src/rpc/manager.ts @@ -131,8 +131,17 @@ class SocketManager { // subscribe to local storage events to see if we need to move the user to the auth page window.addEventListener("storage", e => { if (e.key === "discord_access_token" && !e.newValue) { + this.store.setMe(null); + this.store.setCurrentChannel(null); + this.store.clearUsers(); this.navigate("/"); } + + if (e.key === "user_data" && !e.newValue) { + this.store.setMe(null); + this.store.setCurrentChannel(null); + this.store.clearUsers(); + } }); } From e2cc35b95e1dc2254c9aa81696e72ee0b9cfd415 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sun, 19 Apr 2026 15:21:23 -0400 Subject: [PATCH 10/11] More pleasant flow when auth expires, dev buttons to test --- apps/desktop/src-tauri/src/commands.rs | 15 ++++ apps/desktop/src-tauri/src/main.rs | 1 + apps/desktop/src/rpc/manager.ts | 34 ++++++-- apps/desktop/src/views/settings/account.tsx | 86 ++++++++++++++++++--- 4 files changed, 116 insertions(+), 20 deletions(-) diff --git a/apps/desktop/src-tauri/src/commands.rs b/apps/desktop/src-tauri/src/commands.rs index 00bed9c0..d0d41b93 100644 --- a/apps/desktop/src-tauri/src/commands.rs +++ b/apps/desktop/src-tauri/src/commands.rs @@ -50,6 +50,21 @@ pub fn open_overlay_devtools(window: WebviewWindow) { } } +#[tauri::command] +pub fn simulate_error_screen(window: WebviewWindow) { + let app = window.app_handle(); + + if let Some(main_window) = app.get_webview_window(MAIN_WINDOW_NAME) { + let _ = main_window.eval("window.location.hash = '#/error';"); + let _ = main_window.show(); + let _ = main_window.set_focus(); + } + + if let Some(settings_window) = app.get_webview_window(SETTINGS_WINDOW_NAME) { + let _ = settings_window.hide(); + } +} + #[tauri::command] pub fn toggle_pin( window: WebviewWindow, diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs index d2afb88e..1408674e 100644 --- a/apps/desktop/src-tauri/src/main.rs +++ b/apps/desktop/src-tauri/src/main.rs @@ -168,6 +168,7 @@ fn main() { set_pin, open_devtools, open_overlay_devtools, + simulate_error_screen, close_settings, open_settings, set_hide_taskbar_when_pinned, diff --git a/apps/desktop/src/rpc/manager.ts b/apps/desktop/src/rpc/manager.ts index 474b542c..4714f98c 100644 --- a/apps/desktop/src/rpc/manager.ts +++ b/apps/desktop/src/rpc/manager.ts @@ -52,6 +52,15 @@ class UserdataStore { this.store.removeItem(this.keys.accessToken); this.store.removeItem(this.keys.accessTokenExpiry); } + + removeUserdata() { + this.store.removeItem(this.keys.userData); + } + + clearAuth() { + this.removeAccessToken(); + this.removeUserdata(); + } } /** @@ -337,14 +346,23 @@ class SocketManager { // console.log(payload); // we are ready to do things cause we are fully authed if (payload.cmd === RPCCommand.AUTHENTICATE && payload.evt === RPCEvent.ERROR) { - // they have a token from the old client id - if (payload.data.code === RPCErrors.INVALID_CLIENTID) { - this.userdataStore.removeAccessToken(); - } - - // they have an invalid token - if (payload.data.code === RPCErrors.INVALID_TOKEN) { - this.userdataStore.removeAccessToken(); + // These are recoverable auth issues (e.g. expired/revoked token), + // so clear auth state and send the user back to the reauth screen. + const shouldReauth = [ + RPCErrors.INVALID_CLIENTID, + RPCErrors.INVALID_TOKEN, + RPCErrors.INVALID_USER, + RPCErrors.OAUTH2_ERROR, + ].includes(payload.data.code); + + if (shouldReauth) { + this.userdataStore.clearAuth(); + this.store.setMe(null); + this.store.setCurrentChannel(null); + this.store.clearUsers(); + this.navigate("/"); + track(Metric.DiscordAuthed, 0); + return; } this.store.pushError(payload.data.message); diff --git a/apps/desktop/src/views/settings/account.tsx b/apps/desktop/src/views/settings/account.tsx index 56f55663..c7c60765 100644 --- a/apps/desktop/src/views/settings/account.tsx +++ b/apps/desktop/src/views/settings/account.tsx @@ -26,6 +26,53 @@ import * as shell from "@tauri-apps/plugin-shell"; export const Developer = () => { const platformInfo = usePlatformInfo(); + const DEV_TOKEN_BACKUP_KEY = "overlayed:dev:token-backup"; + const DEV_TOKEN_EXPIRY_BACKUP_KEY = "overlayed:dev:token-expiry-backup"; + const DEV_USER_DATA_BACKUP_KEY = "overlayed:dev:user-data-backup"; + + const simulateTokenExpiryForTesting = async () => { + const currentToken = localStorage.getItem("discord_access_token"); + const currentTokenExpiry = localStorage.getItem("discord_access_token_expiry"); + const currentUserData = localStorage.getItem("user_data"); + + if (currentToken) { + localStorage.setItem(DEV_TOKEN_BACKUP_KEY, currentToken); + } + if (currentTokenExpiry) { + localStorage.setItem(DEV_TOKEN_EXPIRY_BACKUP_KEY, currentTokenExpiry); + } + if (currentUserData) { + localStorage.setItem(DEV_USER_DATA_BACKUP_KEY, currentUserData); + } + + // Clear auth keys so the app immediately routes back to the re-auth screen. + localStorage.removeItem("discord_access_token"); + localStorage.removeItem("discord_access_token_expiry"); + localStorage.removeItem("user_data"); + + // Bring focus back to the main window where the auth screen is shown. + await invoke("close_settings"); + }; + + const restoreTokenAfterTesting = () => { + const tokenBackup = localStorage.getItem(DEV_TOKEN_BACKUP_KEY); + const tokenExpiryBackup = localStorage.getItem(DEV_TOKEN_EXPIRY_BACKUP_KEY); + const userDataBackup = localStorage.getItem(DEV_USER_DATA_BACKUP_KEY); + + if (tokenBackup) { + localStorage.setItem("discord_access_token", tokenBackup); + localStorage.removeItem(DEV_TOKEN_BACKUP_KEY); + } + if (tokenExpiryBackup) { + localStorage.setItem("discord_access_token_expiry", tokenExpiryBackup); + localStorage.removeItem(DEV_TOKEN_EXPIRY_BACKUP_KEY); + } + if (userDataBackup) { + localStorage.setItem("user_data", userDataBackup); + localStorage.removeItem(DEV_USER_DATA_BACKUP_KEY); + } + }; + return ( <>
@@ -35,18 +82,10 @@ export const Developer = () => { variant="outline" onClick={async () => { await invoke("open_devtools"); - }} - > - Open Devtools - - - {import.meta.env.DEV && ( +
+ {import.meta.env.DEV && ( +
- )} -
+ + + +
+ )}
); From 9a47ab5edc00c012ed76c92af243d7452c346c70 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Thu, 7 May 2026 21:05:02 -0400 Subject: [PATCH 11/11] lint/format --- apps/desktop/src-tauri/src/commands.rs | 13 +++++++------ apps/desktop/src-tauri/src/tray.rs | 6 +++--- apps/desktop/src/components/nav-bar.tsx | 26 ++++++++++++------------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/desktop/src-tauri/src/commands.rs b/apps/desktop/src-tauri/src/commands.rs index d0d41b93..f0ef4f6a 100644 --- a/apps/desktop/src-tauri/src/commands.rs +++ b/apps/desktop/src-tauri/src/commands.rs @@ -130,12 +130,13 @@ fn apply_taskbar_visibility(app: &AppHandle, pinned: bool, hide_taskbar_when_pin #[cfg(target_os = "macos")] { use tauri::ActivationPolicy; - app.set_activation_policy(if skip_taskbar { - ActivationPolicy::Accessory - } else { - ActivationPolicy::Regular - }) - .ok(); + app + .set_activation_policy(if skip_taskbar { + ActivationPolicy::Accessory + } else { + ActivationPolicy::Regular + }) + .ok(); } } diff --git a/apps/desktop/src-tauri/src/tray.rs b/apps/desktop/src-tauri/src/tray.rs index ef93d069..602419eb 100644 --- a/apps/desktop/src-tauri/src/tray.rs +++ b/apps/desktop/src-tauri/src/tray.rs @@ -10,9 +10,9 @@ use anyhow::Result; use tauri_plugin_window_state::{AppHandleExt, StateFlags}; use crate::{ - commands, toggle_pin, HideTaskbarWhenPinned, Pinned, TrayMenu, MAIN_WINDOW_NAME, OVERLAYED, SETTINGS_WINDOW_NAME, - TRAY_OPEN_DEVTOOLS_MAIN, TRAY_OPEN_DEVTOOLS_SETTINGS, TRAY_QUIT, TRAY_RELOAD, TRAY_SETTINGS, - TRAY_SHOW_APP, TRAY_TOGGLE_PIN, + commands, toggle_pin, HideTaskbarWhenPinned, Pinned, TrayMenu, MAIN_WINDOW_NAME, OVERLAYED, + SETTINGS_WINDOW_NAME, TRAY_OPEN_DEVTOOLS_MAIN, TRAY_OPEN_DEVTOOLS_SETTINGS, TRAY_QUIT, + TRAY_RELOAD, TRAY_SETTINGS, TRAY_SHOW_APP, TRAY_TOGGLE_PIN, }; pub struct Tray; diff --git a/apps/desktop/src/components/nav-bar.tsx b/apps/desktop/src/components/nav-bar.tsx index 861c149f..f95f45e8 100644 --- a/apps/desktop/src/components/nav-bar.tsx +++ b/apps/desktop/src/components/nav-bar.tsx @@ -112,10 +112,10 @@ export const NavBar = ({ return ( <> Pinning hides this frame to only show the users in the call. -

- Unpin or access Settings anytime via the{" "} - system tray{" "} - icon in the near the clock in your taskbar. +
+
+ Unpin or access Settings anytime via the system tray icon in the near + the clock in your taskbar. ); } @@ -123,19 +123,19 @@ export const NavBar = ({ return ( <> Pinning hides this frame to only show the users in the call. -

- Unpin or access Settings anytime via the{" "} - menu bar{" "} - icon in the top-right of your screen. +
+
+ Unpin or access Settings anytime via the menu bar icon in the + top-right of your screen. ); } return ( <> Pinning hides this frame to only show the users in the call. -

- Unpin or access Settings anytime via the{" "} - system tray /{" "} +
+
+ Unpin or access Settings anytime via the system tray /{" "} notification area icon. ); @@ -211,9 +211,7 @@ export const NavBar = ({ {/* Arrow pointing up toward the pin button */}
-

- {getTrayHint(os)} -

+

{getTrayHint(os)}