Preserve the reviewed shared UI tree and the frozen app dependency commit while
bringing in current main. Port the new keymap UI APIs and use verified central
fonts in its strict UI tests and snapshots.
The suite's egui reports, per key pressed, the character it types on the
Latin layout shortcuts follow (InputState::latin_layout_char). A letter
makes the press that letter; any other character means the key has no
Latin letter, so egui's position fallback no longer turns it into one: on
Dvorak + Russian the key that types й is Dvorak's ', not Ctrl+Q. Without a
report (crates.io egui) the key's position still decides.
The suite's egui-winit reports a letter key on a layout that is not Latin
as the Latin layout's letter, so on Dvorak + Russian the с key is
Dvorak's J. resolve takes that letter before the key's position; with
crates.io egui the letter is the position, as before.
Dvorak's ⇧, types <, which egui has no key for, so egui reports the
press as W and Ctrl+Shift+, ran Ctrl+Shift+W. Once typing shows the
layout's letters are not on QWERTY positions, such a press resolves to
nothing unless the position is known to type its own letter.
Matching, the recorder and labels share one rule (craft-libs#198, track 4):
letters by the layout's Latin letter, or by position on layouts that are not
Latin; digits as typed, else by position; punctuation as typed, without the
Shift the layout needs for it. Layout holds whether the layout is Latin and
its characters at punctuation positions, reported by the platform or learned
from typing. KeyInput holds what the toolkit reports for one press, so a
toolkit that knows the virtual-key code, the macOS command table or the
Shift level can pass it on.
DialogState::show_error shows an app's message under the table until the
user dismisses it (a new Dismiss button on every message) or the next
action replaces it. ForeignSet::truncated carries the entries the app's
reader dropped; plan counts them in ImportReport::truncated, which the
import review shows.
Bindable::import_paths (with_import_path) are read by import matching
like menu paths, and make no areas or groups in the dialog, so a tool's
["Tools", label] path no longer shows a Tools Menu entry.
Outcome, ConflictMode, ProfileError and Options are non_exhaustive, with Options::with for
options built in one expression. Profiles keeps its fields private behind active() and sets(),
lists the sets loading dropped in problems(), and stores a format version (a store without one
reads as version 1).
Profiles keeps the user's sets beside the app's built-in ones: switch, duplicate,
rename, delete, export one set as JSON and import it back, and import another
application's set through an import plan as a new set. The editor model holds an
edit session with undo and cancel, conflict queries for steal or keep, a chord
recorder with double press, and search.
The last segment of a panel-menu entry matches alone only when the panel
name is a segment of some catalogue path. An entry without keys matched
only as a panel-menu item leaves the command's shortcuts alone and is
listed in ImportReport::matched_without_change. Tests cover an entry
without keys next to one with keys for the same command, in both orders.
- ForeignEntry::in_panel_menu marks an item whose leading segments name a panel
(InDesign's "Character: All Caps"); it also matches a catalogue path ending
with its segments, then its last segment as a unique label.
- Aliases count in the menu steps too, joined segments are tried as a label or
alias anywhere, and normalize_label reads a colon as a space, so the alias
"Preferences: General…" matches "Edit > Preferences > General...".
- A leading Show, Hide or Show/Hide is dropped only for commands marked
Bindable::toggle, and only after the names as written found nothing.
- Keys in skipped contexts are left out of unmatched rows, so entries with only
such keys no longer use up the suggestion cap.
conflicts_with lists the commands that share one command's chords, with
that command as a, without filtering every conflict. The README shows
set_bindings with one binding for an editor with one shortcut per
command; assign adds a binding.
Apps that stored { command: text } before read the field in older builds
again. Overrides with several bindings for a command still serialize as
the { command, keys } array. keymap::overrides_list always writes the
array, for craft-settings files, where only an array lets a reset entry
leave the file.
Hit carries the trigger that matched and whether the press matched it
exactly. Dispatcher::dispatch_filtered takes a predicate; a declined hit
leaves its event and text in place.
egui's Copy, Cut and Paste events stay with the app unless
with_clipboard_events(true): egui-winit sends them in place of the
clipboard presses, and a Paste event consumed as a hit lost its text.
With them on, Hit::pasted holds that text, and a web clipboard press
and the event it causes run once for every clipboard chord, not only
Cmd+C/X/V. The Hit doc line moves from DOUBLE_PRESS to Hit.
craft-keymap holds the data and logic for keyboard bindings: egui-free
chords with one canonical text form, bindings to commands with params,
several bindings per command, scopes, tool groups, conflicts, overrides
kept in a craft-settings type, an import pipeline for other applications'
shortcut sets, and an optional egui dispatcher. Apps keep their command
catalogue, file readers and UI.