123 lines
8.1 KiB
Markdown
123 lines
8.1 KiB
Markdown
# Input redesign — keycaps, search, and honest state
|
|
|
|
Approved mock: `home-mocks/input.html` (scratchpad, :8642). Spec wins over mock on conflict.
|
|
|
|
## Goals
|
|
|
|
1. **Shortcuts browser**: the 130-row wall becomes one searchable card — keycap chips, group
|
|
headers with counts, hover-revealed Change/Reset, inline capture. The rebinding engine
|
|
(`Keybinds.rebind`, conflicts refused, `keybindOverrides`) already exists and is untouched.
|
|
2. **Typing card modernized**: dropdowns instead of ChoiceGrid tile walls; raw XKB string and
|
|
variant into a collapsed Advanced section (still discoverable — a contract requires the raw
|
|
string not be hidden *away*, collapsed-but-present satisfies it; C verifies the needle).
|
|
3. **Mouse & Touchpad**: dropdowns over wide segmented rows, four new option groups (below),
|
|
a live try-it area, and a phantom-filtered device list.
|
|
4. **Dictation status-first**: ready hero, hotkeys as keycaps (looked up live from Keybinds so a
|
|
rebind shows truthfully; literal fallback), guided setup steps with a real progress bar, and
|
|
an on-page test field that uses the existing typing pipeline.
|
|
|
|
Non-goals: per-device settings (phantom-heavy device list, zero plumbing — deliberately
|
|
skipped), editing keybinds.lua actions (chords only, as today), touch/tablet options
|
|
(no hardware), accel `custom` curves.
|
|
|
|
## New schema keys (group / hypr option — agent A verifies each option's exact name, type, and
|
|
value map against `hyprctl descriptions` before writing the entry; enum-hypr-map-contract and
|
|
schema-hypr-shape-contract must hold; every key also gets its `prefs.get()` read-back in
|
|
`config/dot/hypr/input.lua` with matching defaults — hypr-prefs-contract):
|
|
|
|
| Key | Group | Hyprland option | UI |
|
|
|---|---|---|---|
|
|
| `focusOnClose` | pointer | `input:focus_on_close` | dropdown: "Most recently used" / "Under the pointer" |
|
|
| `cursorHideWhileTyping` | pointer | `cursor:hide_on_key_press` | toggle "Hide pointer while typing" |
|
|
| `cursorWarpOnWorkspaceChange` | pointer | `cursor:warp_on_change_workspace` | toggle "Jump pointer to the focused display" |
|
|
| `scrollMethod` | pointer | `input:scroll_method` | dropdown (offer only map-published values; include "On a held button" only if the map allows) |
|
|
| `scrollButton` | pointer | `input:scroll_button` | int row, visible only when scrollMethod is the button one |
|
|
| `touchpadClickfinger` | touchpad | `input:touchpad:clickfinger_behavior` | toggle "Two-finger right-click" |
|
|
| `touchpadTapAndDrag` | touchpad | `input:touchpad:tap-and-drag` | toggle "Tap and drag" |
|
|
|
|
Defaults = today's effective Hyprland/input.lua values so shipping changes nothing. If an
|
|
option's published map/type makes a row above impossible as specced (e.g. scroll_method values),
|
|
implement what the map allows and flag the difference loudly.
|
|
|
|
## Service work (A)
|
|
|
|
- `InputDevices.qml`: add `realMice` / `realKeyboards` — name-filtered lists (exclude
|
|
substrings: `consumer-control`, `virtual`, `video-bus`, `power-button`, `webcam`,
|
|
`audio`, `uinput`, plus dedupe transceiver siblings by prefix), each entry `{ name, pretty }`
|
|
(pretty = title-cased, dashes to spaces). Keep the existing flat lists and `hasTouchpad`
|
|
untouched (contract-pinned behavior).
|
|
- Expose `mainKeyboardLayout` (from the main keyboard's live layout string) for the devices card.
|
|
|
|
## UI (B)
|
|
|
|
**ShortcutsPage.qml** (title stays "Keyboard"):
|
|
1. *Typing* card: Layout dropdown (curated common layouts: English (US), English (UK), German,
|
|
French, Spanish, Nordic…, mapping to `keyboardLayout` codes; a stored code outside the list
|
|
renders as the raw code and the dropdown offers "Custom…" which reveals Advanced), Caps Lock
|
|
dropdown, Compose dropdown, Layout-switching dropdown (all four presets keep writing
|
|
`keyboardOptions` through the existing page-local XKB helpers — xkb-presets-contract),
|
|
combined Key repeat row (delay + rate sliders), Num Lock toggle, then **Advanced** expander:
|
|
raw `keyboardOptions` mono field + `keyboardVariant`.
|
|
2. *Shortcuts* card: header "130 bound · N changed" (live counts), subtitle "Click Change and
|
|
press the new keys. A shortcut another action holds is refused, never stolen.", filter field
|
|
(matches description + group, case-insensitive), grouped rows in `Keybinds.groupOrder` with
|
|
"showing N of M" when filtered; each row: description, CHANGED badge when overridden,
|
|
hover-revealed Change/Reset (Reset only on overridden), keycap chord. Capture swaps the
|
|
chord area for the existing `ShortcutCapture` inline. "Restore every shipped shortcut" row
|
|
stays, with the live differs-count detail. Keep the no-GNOME-handoff comment.
|
|
3. New component **KeycapChord.qml**: parses a display chord ("SUPER + SHIFT + Q") into keycap
|
|
chips — mono font, tabular figures, modifier caps tinted accent, "+" separators muted.
|
|
Reused by DictationPage.
|
|
|
|
**MousePage.qml**: Mouse card (speed slow/fast, Acceleration dropdown, Scroll speed, Natural
|
|
scrolling, Left-handed, Middle-click paste, Scroll method dropdown + conditional Scroll button
|
|
row) · Touchpad card (existing rows + the two new toggles; visible on `hasTouchpad`) ·
|
|
Pointer behavior card (Focus dropdown — the 4 followMouse values, focusOnClose dropdown,
|
|
hide-while-typing, hide-after slider with "Never" zero, warp toggle, Pointer size) ·
|
|
**Try it** card (new `InputTestArea.qml`: a scribble Canvas — repaints only on pointer motion,
|
|
cleared by a corner button — and a scrollable text strip; purely local, no compositor writes) ·
|
|
**Connected devices** card from `InputDevices.realKeyboards/realMice` (+ touchpad when
|
|
present), with the subtitle noting phantoms are filtered. Gestures rows fold into the Touchpad
|
|
card (swipe distance + invert) — the separate Gestures card goes.
|
|
|
|
**DictationPage.qml**: ready hero (state tile ✓ / … / ✗, title, model+size line) · hotkey rows
|
|
with KeycapChord, chords looked up from `Keybinds.binds` by matching the dictate descriptions
|
|
(fallback literals if not found) · **Try it** row: a read-only-styled TextField + "Test
|
|
dictation" button that focuses the field and drives `panama-dictate start`/`stop` through the
|
|
existing Dictation service — dictated text lands in the field via the normal wtype pipeline, no
|
|
new script plumbing; detail explains it types here instead of your document · Microphone
|
|
ActionRow → Sound (unchanged) · setup state replaces the hero with numbered steps (container
|
|
image / speech model with progress bar from `downloadFraction` / first transcription), driven
|
|
by the existing `phase` fields; errors keep their row.
|
|
|
|
## Search & docs (C)
|
|
|
|
Extra entries: "Rebind a shortcut" → shortcuts; "Key repeat" auto via schema; "Pointer test
|
|
area" → mouse; "Scroll method" auto; "Connected input devices" → mouse. Existing entries stay.
|
|
Docs + launcher commands regenerate after schema lands (orchestrator).
|
|
|
|
## Contracts (C — write, never run)
|
|
|
|
- `xkb-presets-contract`: verify/adjust needles for the Advanced placement (raw string must
|
|
remain present in the page source).
|
|
- `keybinds-contract` / `keybind-rebind-contract`: UI needles (Change/Reset/ShortcutCapture
|
|
usage) reconciled with the rebuilt page; count-match and conflict rules unchanged.
|
|
- `enum-hypr-map-contract` / `schema-hypr-shape-contract` / `hypr-prefs-contract`: the seven
|
|
new keys must satisfy all three (C statically replays where possible).
|
|
- `settings-pages-contract`, `gnome-handoff-contract` needles re-verified.
|
|
- `dictation-contract`: confirm the test-field flow doesn't violate the "where text lands" pins
|
|
(it uses the normal pipeline; the page merely owns focus). Flag, don't force, if it conflicts.
|
|
- Backlog spec: Phase 8 section.
|
|
|
|
## Agent ownership (parallel)
|
|
|
|
- **A**: `config/PreferenceSchema.qml` (7 new keys), `config/dot/hypr/input.lua`,
|
|
`services/InputDevices.qml`.
|
|
- **B**: `modules/settings/ShortcutsPage.qml`, `MousePage.qml`, `DictationPage.qml`, new
|
|
components (`KeycapChord.qml`, `InputTestArea.qml`, others as needed) + `modules/settings/qmldir`.
|
|
- **C**: `services/SettingsSearch.qml`, the contracts above, backlog spec, README count line
|
|
only if the count changes.
|
|
|
|
B programs against the schema keys and InputDevices API above; A must not change them without
|
|
updating this spec.
|