diff --git a/src/settings/SettingsView.tsx b/src/settings/SettingsView.tsx index 2cd9f49f..9c0edcbd 100644 --- a/src/settings/SettingsView.tsx +++ b/src/settings/SettingsView.tsx @@ -2,8 +2,9 @@ import { useEffect, useRef } from "react"; import { Settings } from "lucide-react"; import type { AppSettings, AppState } from "../types"; import { - SettingGroup, Toggle, accelerationOptions, dwellDelayOptions, keyRepeatDelayOptions, - movementValue, pointerSpeedOptions, pointerSpeedValues, repeatIntervalOptions, + OptionGroup, SettingGroup, Toggle, accelerationOptions, dwellDelayOptions, keyRepeatDelayOptions, + movementValue, overlaySizeOptions, overlayVisibilityOptions, pointerSpeedOptions, + pointerSpeedValues, repeatIntervalOptions, secondsOptions, } from "./controls"; import { UpdateControls, type UpdateAction } from "./UpdatesSection"; @@ -29,47 +30,33 @@ export function SettingsView({ state, settings, onChange, chooseTelemetry, updat
update("mouseRepeatEnabled", value)} />
-
Movement interval
- {repeatIntervalOptions.map((value) => )} -
-
Movement acceleration
- {accelerationOptions.map(({ value, label }) => )} -
-
Scroll interval
- {repeatIntervalOptions.map((value) => )} -
+ legend="Movement interval" columns="four" disabled={!settings.mouseRepeatEnabled} options={secondsOptions(repeatIntervalOptions)} value={settings.moveRepeatIntervalMs} onChange={(next) => update("moveRepeatIntervalMs", next)} /> + legend="Movement acceleration" columns="four" disabled={!settings.mouseRepeatEnabled} options={accelerationOptions} value={settings.mouseRepeatAccelerationDurationMs} onChange={(next) => update("mouseRepeatAccelerationDurationMs", next)} /> + legend="Scroll interval" columns="four" disabled={!settings.mouseRepeatEnabled} options={secondsOptions(repeatIntervalOptions)} value={settings.scrollRepeatIntervalMs} onChange={(next) => update("scrollRepeatIntervalMs", next)} />
update("keyRepeatEnabled", value)} />
-
Delay before repeating
- {keyRepeatDelayOptions.map(({ value, label }) => )} -
-
Key interval
- {repeatIntervalOptions.map((value) => )} -
+ legend="Delay before repeating" columns="four" disabled={!settings.keyRepeatEnabled} options={keyRepeatDelayOptions} value={settings.keyRepeatInitialDelayMs} onChange={(next) => update("keyRepeatInitialDelayMs", next)} /> + legend="Key interval" columns="four" disabled={!settings.keyRepeatEnabled} options={secondsOptions(repeatIntervalOptions)} value={settings.keyRepeatIntervalMs} onChange={(next) => update("keyRepeatIntervalMs", next)} />

Holding a navigation key on the remote repeats it, like holding a key on a keyboard. Applies to the arrow keys, Tab, Backspace, Delete, Page Up, and Page Down.

update("dwellClickEnabled", value)} />
-
Dwell delay
- {dwellDelayOptions.map((value) => )} -
+ legend="Dwell delay" columns="five" disabled={!settings.dwellClickEnabled} options={secondsOptions(dwellDelayOptions)} value={settings.dwellClickDelayMs} onChange={(next) => update("dwellClickDelayMs", next)} />

After Android pointer movement stops, a countdown appears and performs one left click. Move again to rearm it.

{state.capabilities.cursorOverlay && <> update("cursorOverlayEnabled", value)} />
-
Overlay visibility
- {(["onInput", "whileControlling"] as const).map((value) => )} -

On input hides shortly after pointer activity stops. While controlling stays visible until the session ends.

-
Overlay size
- {(["small", "medium", "large"] as const).map((value) => )} -
+ legend="Overlay visibility" disabled={!settings.cursorOverlayEnabled} options={overlayVisibilityOptions} value={settings.cursorOverlayVisibility} onChange={(next) => update("cursorOverlayVisibility", next)}> +

On input hides shortly after pointer activity stops. While controlling stays visible until the session ends.

+ + legend="Overlay size" columns="three" disabled={!settings.cursorOverlayEnabled} options={overlaySizeOptions} value={settings.cursorOverlaySize} onChange={(next) => update("cursorOverlaySize", next)} />
Overlay color
{(["red", "green", "blue", "yellow", "white"] as const).map((value) => )}
diff --git a/src/settings/controls.tsx b/src/settings/controls.tsx index 905c0c35..dd091bbe 100644 --- a/src/settings/controls.tsx +++ b/src/settings/controls.tsx @@ -30,3 +30,29 @@ export function movementValue(base: number, scale: number) { const value = Math.min(50, Math.max(1, Math.round((base * scale / 100) * 2) / 2)); return Number.isInteger(value) ? String(value) : value.toFixed(1); } + +export function OptionGroup({ legend, options, value, onChange, disabled, columns, children }: { + legend: string; + options: ReadonlyArray<{ value: T; label: ReactNode }>; + value: T; + onChange: (next: T) => void; + disabled: boolean; + columns?: "three" | "four" | "five"; + children?: ReactNode; +}) { + return
{legend}
+ {options.map((option) => )} +
{children}
; +} + +export function secondsOptions(values: readonly T[]) { + return values.map((value) => ({ value, label: <>{value / 1000}s })); +} + +export const overlayVisibilityOptions = [ + { value: "onInput", label: "On input" }, + { value: "whileControlling", label: "While controlling" }, +] as const; + +export const overlaySizeOptions = (["small", "medium", "large"] as const) + .map((value) => ({ value, label: value[0].toUpperCase() + value.slice(1) }));