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>
58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
import clsx from 'clsx';
|
|
import React from 'react';
|
|
import SectionTitle from './SectionTitle';
|
|
|
|
interface BoxedListProps {
|
|
/**
|
|
* Optional small-uppercase label above the boxed list (Adwaita
|
|
* AdwPreferencesGroup style). Style is fixed: caller passes the string.
|
|
*/
|
|
title?: string;
|
|
/**
|
|
* Optional one-line description rendered between the title and the list.
|
|
* Use sparingly — most groups need just the label.
|
|
*/
|
|
description?: React.ReactNode;
|
|
/** Child rows — typically `<SettingsRow>` / `<SettingsSwitchRow>` / `<NavigationRow>`. */
|
|
children: React.ReactNode;
|
|
/** Outer wrapper className (spacing, data-setting-id ancestor, etc.). */
|
|
className?: string;
|
|
/** Inner card className (borders, bg, etc.). */
|
|
cardClassName?: string;
|
|
/** Inner wrapper className (padding, etc.). */
|
|
innerClassName?: string;
|
|
/** Forwarded to the outer wrapper for command-palette deep-linking. */
|
|
'data-setting-id'?: string;
|
|
}
|
|
|
|
/**
|
|
* Adwaita-style `AdwPreferencesGroup` container. Renders an optional small
|
|
* uppercase title + description, then the boxed-list card with `divide-y`
|
|
* rows inside. See DESIGN.md §5.
|
|
*/
|
|
const BoxedList: React.FC<BoxedListProps> = ({
|
|
title,
|
|
description,
|
|
children,
|
|
className,
|
|
cardClassName,
|
|
innerClassName,
|
|
'data-setting-id': dataSettingId,
|
|
}) => {
|
|
return (
|
|
<div className={clsx('w-full', className)} data-setting-id={dataSettingId}>
|
|
{title && <SectionTitle className='mb-2'>{title}</SectionTitle>}
|
|
<div className={clsx('card eink-bordered border-base-200 bg-base-100 border', cardClassName)}>
|
|
<div className={clsx('divide-base-200 divide-y ps-4', innerClassName)}>{children}</div>
|
|
</div>
|
|
{description && (
|
|
<p className='text-base-content/65 mb-2 mt-1 ps-4 text-[0.8em] leading-relaxed'>
|
|
{description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default BoxedList;
|