mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-27 13:06:20 +02:00
* feat(shortcuts): make browser-reserved accelerators recordable on desktop Cmd/Ctrl+P (and L/T/N/D/U) were rejected by the shortcut recorder on every platform because a browser tab cannot reliably own them. The Electron renderer receives these as plain keydowns — neither Electron's default menu nor the desktop shell binds any of them — so the reservation now only applies to the web runtime. Adds a ShortcutRuntime dimension (configured by CoreProvider from the client identity, with a preload-global fallback that is already correct at module-eval time so store hydration sanitizes with the right runtime). App-owned accelerators (W/R/Q, editing keys, zoom row) stay reserved everywhere. Closes MUL-4457 Co-authored-by: multica-agent <github@multica.ai> * fix(shortcuts): limit desktop unlock to bare primary browser accelerators Review follow-up (MUL-4457): the desktop skip matched primary+key with any extra modifiers, which would also unreserve OS-owned combos such as Option+Cmd+D (macOS Dock toggle) and Ctrl+Alt+T (Linux terminal). Only the bare primary chord is now recordable on desktop; every extra-modifier variant keeps the historical reservation on both runtimes. Adds regression tests for the OS combos. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Lambda <lambda@multica.ai> Co-authored-by: multica-agent <github@multica.ai>
76 lines
2.7 KiB
TypeScript
76 lines
2.7 KiB
TypeScript
export type ShortcutPlatform = "macos" | "windows" | "linux" | "unknown";
|
|
|
|
/** Where shortcut handling runs: a browser tab or the Electron renderer. */
|
|
export type ShortcutRuntime = "web" | "desktop";
|
|
|
|
let configuredPlatform: ShortcutPlatform | null = null;
|
|
let configuredRuntime: ShortcutRuntime | null = null;
|
|
|
|
function normalizePlatform(value: string): ShortcutPlatform {
|
|
const platform = value.toLowerCase();
|
|
if (platform.includes("mac") || platform.includes("darwin")) return "macos";
|
|
if (platform.includes("win")) return "windows";
|
|
if (platform.includes("linux") || platform.includes("x11")) return "linux";
|
|
return "unknown";
|
|
}
|
|
|
|
/** Browser fallback. Desktop injects its authoritative OS through CoreProvider. */
|
|
export function detectShortcutPlatform(): ShortcutPlatform {
|
|
if (typeof navigator === "undefined") return "unknown";
|
|
const nav = navigator as Navigator & {
|
|
userAgentData?: { platform?: string };
|
|
};
|
|
// Some privacy-hardened browsers expose `platform` as an empty string.
|
|
// Nullish coalescing would stop there and discard a still-useful userAgent,
|
|
// causing Command to be interpreted as Windows/Super on a Mac.
|
|
const signals = [nav.userAgentData?.platform, nav.platform, nav.userAgent];
|
|
for (const signal of signals) {
|
|
if (!signal?.trim()) continue;
|
|
const platform = normalizePlatform(signal);
|
|
if (platform !== "unknown") return platform;
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
export function configureShortcutPlatform(
|
|
platform: ShortcutPlatform | null | undefined,
|
|
): void {
|
|
configuredPlatform = platform ?? null;
|
|
}
|
|
|
|
export function getShortcutPlatform(): ShortcutPlatform {
|
|
return configuredPlatform ?? detectShortcutPlatform();
|
|
}
|
|
|
|
/**
|
|
* Environment fallback, and unlike the OS it must be trustworthy at
|
|
* module-evaluation time: the shortcut store hydrates (and sanitizes
|
|
* persisted overrides) when its module first loads, before CoreProvider
|
|
* gets a chance to configure anything. That is safe because the desktop
|
|
* preload exposes its bridge globals before any renderer script runs.
|
|
* Same signals as `detectClientType` in ../analytics — not imported so
|
|
* this module stays dependency-free.
|
|
*/
|
|
export function detectShortcutRuntime(): ShortcutRuntime {
|
|
if (typeof window === "undefined") return "web";
|
|
const w = window as unknown as { electron?: unknown; desktopAPI?: unknown };
|
|
if (w.electron || w.desktopAPI) return "desktop";
|
|
if (
|
|
typeof navigator !== "undefined" &&
|
|
/Electron/i.test(navigator.userAgent)
|
|
) {
|
|
return "desktop";
|
|
}
|
|
return "web";
|
|
}
|
|
|
|
export function configureShortcutRuntime(
|
|
runtime: ShortcutRuntime | null | undefined,
|
|
): void {
|
|
configuredRuntime = runtime ?? null;
|
|
}
|
|
|
|
export function getShortcutRuntime(): ShortcutRuntime {
|
|
return configuredRuntime ?? detectShortcutRuntime();
|
|
}
|