Files
OwnCord/Client/tauri-client/src/lib/window-state.ts
T
J3vbandClaude Opus 4.8 f3c6745c0b feat(client): add single-instance/autostart/deep-link; move window-state to plugin
Replace hand-rolled code with first-party Tauri v2 plugins where a plugin can
do the job, and add the genuine gaps:

- single-instance: focus the running window on a second launch instead of
  opening a duplicate (two WS connections / tray icons). Registered first;
  built with the "deep-link" feature so owncord:// links reach the running app.
- window-state: replace the hand-rolled save/restore plumbing with
  tauri-plugin-window-state. Keep only the one thing the plugin lacks — an
  off-screen re-center guard for windows restored onto a now-disconnected
  monitor (isRectOnScreen).
- autostart: "Launch on Login" toggle in Advanced settings, reading/writing
  real OS state via tauri-plugin-autostart (not a stored preference).
- deep-link: register the owncord:// scheme and route invite links into the
  register form. OwnCord invites are registration invites, so a link pre-fills
  and opens the form rather than completing a one-click join.

Intentionally NOT replaced: push-to-talk (ptt.rs) stays hand-rolled —
tauri-plugin-global-shortcut registers OS hotkeys that grab the key
system-wide (RegisterHotKey / XGrabKey), which cannot express non-consuming
press-and-hold PTT. Clipboard stays on the native Web API (no custom code).

Verified: tsc, eslint, prettier, 3369 unit tests, cargo check, cargo clippy
(client code clean; one pre-existing needless-borrow lint in commands.rs is
flagged only by newer local clippy, untouched here).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 10:44:06 +02:00

93 lines
3.1 KiB
TypeScript

/**
* Window-state off-screen guard.
*
* Window geometry (size / position / maximized) is persisted and restored by
* `tauri-plugin-window-state` on the Rust side. That plugin does NOT validate
* the restored position against the current monitor layout, so this module adds
* the one thing it lacks: if the restored window landed off every connected
* monitor (e.g. it was last closed on a display that is now disconnected),
* re-center it so it can't become unreachable.
*/
import { createLogger } from "./logger";
const log = createLogger("window-state");
/** A window rectangle in physical pixels. */
export interface WindowRect {
readonly x: number;
readonly y: number;
readonly width: number;
readonly height: number;
}
/** Minimum horizontal overlap (physical px) required with some monitor. */
const MIN_VISIBLE_WIDTH = 100;
/** Allow the title bar to sit slightly above a monitor's top edge. */
const TITLEBAR_TOP_TOLERANCE = 8;
/** The title bar must be at least this far above a monitor's bottom edge. */
const TITLEBAR_GRAB_MARGIN = 40;
interface MonitorRect {
readonly position: { x: number; y: number };
readonly size: { width: number; height: number };
}
/**
* Check whether a window rect is reachable on one of the given monitors:
* enough horizontal overlap to grab, and the title bar row within the
* monitor's vertical range. All values are physical pixels.
*/
export function isRectOnScreen(monitors: readonly MonitorRect[], rect: WindowRect): boolean {
return monitors.some((m) => {
const overlapX =
Math.min(rect.x + rect.width, m.position.x + m.size.width) - Math.max(rect.x, m.position.x);
const titleBarReachable =
rect.y >= m.position.y - TITLEBAR_TOP_TOLERANCE &&
rect.y <= m.position.y + m.size.height - TITLEBAR_GRAB_MARGIN;
return overlapX >= MIN_VISIBLE_WIDTH && titleBarReachable;
});
}
/**
* After `tauri-plugin-window-state` restores the window, re-center it if it
* landed off-screen. Fire-and-forget; a no-op outside Tauri. Fails open: if
* monitors can't be queried the plugin's placement is left untouched.
*/
export async function initWindowState(): Promise<void> {
let tauriWindow: typeof import("@tauri-apps/api/window");
try {
tauriWindow = await import("@tauri-apps/api/window");
} catch {
return;
}
const win = tauriWindow.getCurrentWindow();
try {
// A maximized window fills a monitor by definition — nothing to correct.
if (await win.isMaximized()) return;
let monitors: MonitorRect[];
try {
monitors = await tauriWindow.availableMonitors();
} catch (err) {
log.warn("Could not query monitors; leaving restored window as-is", {
error: String(err),
});
return;
}
if (monitors.length === 0) return;
const pos = await win.outerPosition();
const size = await win.outerSize();
const rect: WindowRect = { x: pos.x, y: pos.y, width: size.width, height: size.height };
if (!isRectOnScreen(monitors, rect)) {
log.warn("Restored window is off-screen; re-centering", rect);
await win.center();
}
} catch (err) {
log.warn("Window-state off-screen check failed", { error: String(err) });
}
}