forked from akai/readest
787bbf2103
* feat(reader): add custom hardware-button page turning (#4139) Lets users bind hardware remote keys (media keys, D-pad/arrow keys) to previous/next page via a learn-mode capture UI in reader settings — an accessibility feature for page-turner remotes. - New global hardwarePageTurner system setting (enabled + key bindings). - hardwareKeys.ts: key normalization, matching, and page-turn resolution. - deviceStore: reference-counted media-key interception + learn mode. - usePagination: flips pages from bound media keys (native bridge) and D-pad/keyboard keys (DOM keydown), scoped to the active book and suppressed while the toolbar is visible. - Page Turner settings section on all platforms; web/desktop bind keys via DOM keydown only, native media-key interception stays mobile-only. - Android: intercept media + learn-mode keys in dispatchKeyEvent. - iOS: forward media keys via MPRemoteCommandCenter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(i18n): add and translate hardware page turner strings (#4139) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(reader): refine hardware page turner (#4139) - Handle book-iframe key events (iframe-keydown messages) so custom bindings work as soon as a book is open, not only after the settings panel has been shown. - Add Previous/Next Section bindings alongside the page bindings. - Rename the hardwareKeys util to keybinding. - Wire the Page Turner section into the settings Reset action. - Drop the focus ring on the capture buttons; BoxedList gains an optional description. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(i18n): translate page turner section and key strings (#4139) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
266 lines
6.9 KiB
TypeScript
266 lines
6.9 KiB
TypeScript
import { invoke } from '@tauri-apps/api/core';
|
|
|
|
export interface CopyURIRequest {
|
|
uri: string;
|
|
dst: string;
|
|
}
|
|
|
|
export interface CopyURIResponse {
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
export interface UseBackgroundAudioRequest {
|
|
enabled: boolean;
|
|
}
|
|
|
|
export interface InstallPackageRequest {
|
|
path: string;
|
|
}
|
|
|
|
export interface InstallPackageResponse {
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
export interface SetSystemUIVisibilityRequest {
|
|
visible: boolean;
|
|
darkMode: boolean;
|
|
}
|
|
|
|
export interface SetSystemUIVisibilityResponse {
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
export interface GetStatusBarHeightResponse {
|
|
height: number;
|
|
error?: string;
|
|
}
|
|
|
|
export interface GetSystemFontsListResponse {
|
|
fonts: Record<string, string>; // { fontName: fontFamily }
|
|
error?: string;
|
|
}
|
|
|
|
export interface InterceptKeysRequest {
|
|
volumeKeys?: boolean;
|
|
backKey?: boolean;
|
|
/** Intercept media keys (next/previous/play-pause) for the hardware page turner. */
|
|
pageTurnerKeys?: boolean;
|
|
/** Forward every key press to JS so the settings UI can capture a binding. */
|
|
learnMode?: boolean;
|
|
}
|
|
|
|
export interface LockScreenRequest {
|
|
orientation: 'portrait' | 'landscape' | 'auto';
|
|
}
|
|
|
|
export interface GetSystemColorSchemeResponse {
|
|
colorScheme: 'light' | 'dark';
|
|
error?: string;
|
|
}
|
|
|
|
export interface GetSafeAreaInsetsResponse {
|
|
top: number;
|
|
right: number;
|
|
bottom: number;
|
|
left: number;
|
|
error?: string;
|
|
}
|
|
|
|
interface GetScreenBrightnessResponse {
|
|
brightness: number; // 0.0 to 1.0
|
|
error?: string;
|
|
}
|
|
|
|
interface SetScreenBrightnessRequest {
|
|
brightness: number; // 0.0 to 1.0
|
|
}
|
|
|
|
interface SetScreenBrightnessResponse {
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
interface GetExternalSDCardPathResponse {
|
|
path: string | null;
|
|
error?: string;
|
|
}
|
|
|
|
interface SelectDirectoryResponse {
|
|
cancelled?: boolean;
|
|
uri?: string;
|
|
path?: string;
|
|
error?: string;
|
|
}
|
|
|
|
export interface GetStorefrontRegionCodeResponse {
|
|
regionCode?: string;
|
|
error?: string;
|
|
}
|
|
|
|
export async function copyURIToPath(request: CopyURIRequest): Promise<CopyURIResponse> {
|
|
const result = await invoke<CopyURIResponse>('plugin:native-bridge|copy_uri_to_path', {
|
|
payload: request,
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
export async function invokeUseBackgroundAudio(request: UseBackgroundAudioRequest): Promise<void> {
|
|
await invoke('plugin:native-bridge|use_background_audio', {
|
|
payload: request,
|
|
});
|
|
}
|
|
|
|
export async function installPackage(
|
|
request: InstallPackageRequest,
|
|
): Promise<InstallPackageResponse> {
|
|
const result = await invoke<InstallPackageResponse>('plugin:native-bridge|install_package', {
|
|
payload: request,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
export async function setSystemUIVisibility(
|
|
request: SetSystemUIVisibilityRequest,
|
|
): Promise<SetSystemUIVisibilityResponse> {
|
|
const result = await invoke<SetSystemUIVisibilityResponse>(
|
|
'plugin:native-bridge|set_system_ui_visibility',
|
|
{
|
|
payload: request,
|
|
},
|
|
);
|
|
return result;
|
|
}
|
|
|
|
export async function getStatusBarHeight(): Promise<GetStatusBarHeightResponse> {
|
|
const result = await invoke<GetStatusBarHeightResponse>(
|
|
'plugin:native-bridge|get_status_bar_height',
|
|
);
|
|
return result;
|
|
}
|
|
|
|
let cachedSysFontsResult: GetSystemFontsListResponse | null = null;
|
|
|
|
export async function getSysFontsList(): Promise<GetSystemFontsListResponse> {
|
|
if (cachedSysFontsResult) {
|
|
return cachedSysFontsResult;
|
|
}
|
|
const result = await invoke<GetSystemFontsListResponse>(
|
|
'plugin:native-bridge|get_sys_fonts_list',
|
|
);
|
|
cachedSysFontsResult = result;
|
|
return result;
|
|
}
|
|
|
|
export async function interceptKeys(request: InterceptKeysRequest): Promise<void> {
|
|
await invoke('plugin:native-bridge|intercept_keys', {
|
|
payload: request,
|
|
});
|
|
}
|
|
|
|
export async function lockScreenOrientation(request: LockScreenRequest): Promise<void> {
|
|
await invoke('plugin:native-bridge|lock_screen_orientation', {
|
|
payload: request,
|
|
});
|
|
}
|
|
|
|
export async function getSystemColorScheme(): Promise<GetSystemColorSchemeResponse> {
|
|
const result = await invoke<GetSystemColorSchemeResponse>(
|
|
'plugin:native-bridge|get_system_color_scheme',
|
|
);
|
|
return result;
|
|
}
|
|
|
|
export async function getSafeAreaInsets(): Promise<GetSafeAreaInsetsResponse> {
|
|
const result = await invoke<GetSafeAreaInsetsResponse>(
|
|
'plugin:native-bridge|get_safe_area_insets',
|
|
);
|
|
return result;
|
|
}
|
|
|
|
export async function getScreenBrightness(): Promise<GetScreenBrightnessResponse> {
|
|
const result = await invoke<GetScreenBrightnessResponse>(
|
|
'plugin:native-bridge|get_screen_brightness',
|
|
);
|
|
return result;
|
|
}
|
|
|
|
export async function setScreenBrightness(
|
|
request: SetScreenBrightnessRequest,
|
|
): Promise<SetScreenBrightnessResponse> {
|
|
const result = await invoke<SetScreenBrightnessResponse>(
|
|
'plugin:native-bridge|set_screen_brightness',
|
|
{
|
|
payload: request,
|
|
},
|
|
);
|
|
return result;
|
|
}
|
|
|
|
export async function getExternalSDCardPath(): Promise<GetExternalSDCardPathResponse> {
|
|
const result = await invoke<GetExternalSDCardPathResponse>(
|
|
'plugin:native-bridge|get_external_sdcard_path',
|
|
);
|
|
return result;
|
|
}
|
|
|
|
export async function selectDirectory(): Promise<SelectDirectoryResponse> {
|
|
const result = await invoke<SelectDirectoryResponse>('plugin:native-bridge|select_directory');
|
|
return result;
|
|
}
|
|
|
|
export async function getStorefrontRegionCode(): Promise<GetStorefrontRegionCodeResponse> {
|
|
const result = await invoke<GetStorefrontRegionCodeResponse>(
|
|
'plugin:native-bridge|get_storefront_region_code',
|
|
);
|
|
return result;
|
|
}
|
|
|
|
// ── Sync passphrase keychain ────────────────────────────────────────────
|
|
// Tauri-only. Wired into the TauriPassphraseStore (src/libs/crypto/
|
|
// passphrase.ts) so the user's sync passphrase persists across app
|
|
// launches via the OS keychain (macOS Keychain, Windows Credential
|
|
// Manager, Linux libsecret, iOS Keychain, Android EncryptedSharedPrefs).
|
|
|
|
export interface SetSyncPassphraseRequest {
|
|
passphrase: string;
|
|
}
|
|
|
|
export interface SyncPassphraseResponse {
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
export interface GetSyncPassphraseResponse {
|
|
passphrase?: string;
|
|
error?: string;
|
|
}
|
|
|
|
export interface SyncKeychainAvailableResponse {
|
|
available: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
export async function setSyncPassphrase(
|
|
request: SetSyncPassphraseRequest,
|
|
): Promise<SyncPassphraseResponse> {
|
|
return invoke<SyncPassphraseResponse>('plugin:native-bridge|set_sync_passphrase', {
|
|
payload: request,
|
|
});
|
|
}
|
|
|
|
export async function getSyncPassphrase(): Promise<GetSyncPassphraseResponse> {
|
|
return invoke<GetSyncPassphraseResponse>('plugin:native-bridge|get_sync_passphrase');
|
|
}
|
|
|
|
export async function clearSyncPassphrase(): Promise<SyncPassphraseResponse> {
|
|
return invoke<SyncPassphraseResponse>('plugin:native-bridge|clear_sync_passphrase');
|
|
}
|
|
|
|
export async function isSyncKeychainAvailable(): Promise<SyncKeychainAvailableResponse> {
|
|
return invoke<SyncKeychainAvailableResponse>('plugin:native-bridge|is_sync_keychain_available');
|
|
}
|