mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-23 09:08:32 +02:00
Refactor proxy codebase
This commit is contained in:
62
apps/yaak-proxy/lib/hotkeys.ts
Normal file
62
apps/yaak-proxy/lib/hotkeys.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { ActionInvocation, ActionMetadata } from '@yaakapp-internal/proxy-lib';
|
||||
import { rpc } from './rpc';
|
||||
|
||||
type ActionBinding = {
|
||||
invocation: ActionInvocation;
|
||||
meta: ActionMetadata;
|
||||
keys: { key: string; ctrl: boolean; shift: boolean; alt: boolean; meta: boolean };
|
||||
};
|
||||
|
||||
/** Parse a hotkey string like "Ctrl+Shift+P" into its parts. */
|
||||
function parseHotkey(hotkey: string): ActionBinding['keys'] {
|
||||
const parts = hotkey.split('+').map((p) => p.trim().toLowerCase());
|
||||
return {
|
||||
ctrl: parts.includes('ctrl') || parts.includes('control'),
|
||||
shift: parts.includes('shift'),
|
||||
alt: parts.includes('alt'),
|
||||
meta: parts.includes('meta') || parts.includes('cmd') || parts.includes('command'),
|
||||
key:
|
||||
parts.filter(
|
||||
(p) => !['ctrl', 'control', 'shift', 'alt', 'meta', 'cmd', 'command'].includes(p),
|
||||
)[0] ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
function matchesEvent(binding: ActionBinding['keys'], e: KeyboardEvent): boolean {
|
||||
return (
|
||||
e.ctrlKey === binding.ctrl &&
|
||||
e.shiftKey === binding.shift &&
|
||||
e.altKey === binding.alt &&
|
||||
e.metaKey === binding.meta &&
|
||||
e.key.toLowerCase() === binding.key
|
||||
);
|
||||
}
|
||||
|
||||
/** Fetch all actions from Rust and register a global keydown listener. */
|
||||
export async function initHotkeys(): Promise<() => void> {
|
||||
const { actions } = await rpc('list_actions', {});
|
||||
|
||||
const bindings: ActionBinding[] = actions
|
||||
.filter(
|
||||
(entry): entry is [ActionInvocation, ActionMetadata & { defaultHotkey: string }] =>
|
||||
entry[1].defaultHotkey != null,
|
||||
)
|
||||
.map(([invocation, meta]) => ({
|
||||
invocation,
|
||||
meta,
|
||||
keys: parseHotkey(meta.defaultHotkey),
|
||||
}));
|
||||
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
for (const binding of bindings) {
|
||||
if (matchesEvent(binding.keys, e)) {
|
||||
e.preventDefault();
|
||||
rpc('execute_action', binding.invocation);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => window.removeEventListener('keydown', onKeyDown);
|
||||
}
|
||||
24
apps/yaak-proxy/lib/rpc.ts
Normal file
24
apps/yaak-proxy/lib/rpc.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen as tauriListen } from '@tauri-apps/api/event';
|
||||
import type { RpcEventSchema, RpcSchema } from '@yaakapp-internal/proxy-lib';
|
||||
|
||||
export type Req<K extends keyof RpcSchema> = RpcSchema[K][0];
|
||||
export type Res<K extends keyof RpcSchema> = RpcSchema[K][1];
|
||||
|
||||
export async function rpc<K extends keyof RpcSchema>(cmd: K, payload: Req<K>): Promise<Res<K>> {
|
||||
return invoke('rpc', { cmd, payload }) as Promise<Res<K>>;
|
||||
}
|
||||
|
||||
/** Subscribe to a backend event. Returns an unsubscribe function. */
|
||||
export function listen<K extends keyof RpcEventSchema>(
|
||||
event: K & string,
|
||||
callback: (payload: RpcEventSchema[K]) => void,
|
||||
): () => void {
|
||||
let unsub: (() => void) | null = null;
|
||||
tauriListen<RpcEventSchema[K]>(event, (e) => callback(e.payload))
|
||||
.then((fn) => {
|
||||
unsub = fn;
|
||||
})
|
||||
.catch(console.error);
|
||||
return () => unsub?.();
|
||||
}
|
||||
15
apps/yaak-proxy/lib/store.ts
Normal file
15
apps/yaak-proxy/lib/store.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { createModelStore } from "@yaakapp-internal/model-store";
|
||||
import type { HttpExchange } from "@yaakapp-internal/proxy-lib";
|
||||
|
||||
type ProxyModels = {
|
||||
http_exchange: HttpExchange;
|
||||
};
|
||||
|
||||
export const { dataAtom, applyChange, replaceAll, listAtom, orderedListAtom } =
|
||||
createModelStore<ProxyModels>(["http_exchange"]);
|
||||
|
||||
export const httpExchangesAtom = orderedListAtom(
|
||||
"http_exchange",
|
||||
"createdAt",
|
||||
"desc",
|
||||
);
|
||||
37
apps/yaak-proxy/lib/theme.ts
Normal file
37
apps/yaak-proxy/lib/theme.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { setWindowTheme } from "@yaakapp-internal/mac-window";
|
||||
import {
|
||||
applyThemeToDocument,
|
||||
defaultDarkTheme,
|
||||
defaultLightTheme,
|
||||
getCSSAppearance,
|
||||
platformFromUserAgent,
|
||||
setPlatformOnDocument,
|
||||
subscribeToPreferredAppearance,
|
||||
type Appearance,
|
||||
} from "@yaakapp-internal/theme";
|
||||
|
||||
export function initTheme() {
|
||||
setPlatformOnDocument(platformFromUserAgent(navigator.userAgent));
|
||||
|
||||
// Apply a quick initial theme based on CSS media query
|
||||
let preferredAppearance: Appearance = getCSSAppearance();
|
||||
applyTheme(preferredAppearance);
|
||||
|
||||
// Then subscribe to accurate OS appearance detection and changes
|
||||
subscribeToPreferredAppearance((a) => {
|
||||
preferredAppearance = a;
|
||||
applyTheme(preferredAppearance);
|
||||
});
|
||||
|
||||
// Show window after initial theme is applied (window starts hidden to prevent flash)
|
||||
getCurrentWebviewWindow().show().catch(console.error);
|
||||
}
|
||||
|
||||
function applyTheme(appearance: Appearance) {
|
||||
const theme = appearance === "dark" ? defaultDarkTheme : defaultLightTheme;
|
||||
applyThemeToDocument(theme);
|
||||
if (theme.base.surface != null) {
|
||||
setWindowTheme(theme.base.surface);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user