Bare literal assignments (drag_overlay_mode = 'axis_y') cause mypy to
narrow-infer the type as Literal['axis_y'] on the parent class, making
the subclass override (Horizontal = 'axis_x', Fat = 'axis_x') an
incompatible assignment. Fix by explicitly annotating Vertical and Tall
with the full union type from the base class, so the declared type stays
wide and subclasses can freely assign any valid mode.
Also removes unused ClassVar/Literal imports from splits.py.
Horizontal extends Vertical, and Fat extends Tall. Declaring
drag_overlay_mode with a narrower Literal type in the subclass
conflicts with the parent's declared type, causing mypy error
"Incompatible types in assignment". Since the base Layout class already
declares the full union type, subclasses only need a bare assignment.
Also removes now-unused ClassVar and Literal imports from vertical.py,
tall.py, and grid.py.
Previously, body drops in all non-Splits layouts showed a full-window overlay
and performed a positional swap. This adds proper top/bottom or left/right
half-window overlays and true before/after insertion for the five layouts
Kovid identified.
Architecture:
- New `drag_overlay_mode` ClassVar on Layout ('full'|'axis_y'|'axis_x'|'free')
controls both overlay display and valid direction axis. Layout subclasses set
one line; tabs.py and boss.py dispatch on this attribute instead of hasattr.
- New `insert_window_group_next_to(target_group_id, after)` on WindowList
performs a positional insert (not swap) by popping the active group and
inserting it before or after the target.
- New base `insert_window_next_to` on Layout uses insert_window_group_next_to
for axis_x/axis_y layouts and falls back to swap for 'full' (Stack).
Splits overrides this with its existing tree-based implementation.
- `_insert_window_in_direction` in boss.py collapses from a 7-line hasattr
branch to a single layout.insert_window_next_to() call.
Direction constraints:
Vertical, Tall, Grid -> top/bottom (axis_y)
Horizontal, Fat -> left/right (axis_x)
Splits -> 4-way free (unchanged)
Stack -> full-window swap (unchanged)
Add --allow-fallback option to the map command that controls shifted
and ascii (alternate_key) fallback for individual key mappings.
For non-Latin keyboard layouts, when the current layout key is
non-ascii (codepoint > 127 and < 0xE000), the alternate_key from
the base layout is used for matching if the mapping opts in via
--allow-fallback=shifted,ascii.
Default kitty bindings use --allow-fallback=shifted,ascii so they
work out of the box with non-Latin layouts. User custom mappings
default to --allow-fallback=shifted (preserving existing shifted_key
behavior without ascii fallback).
--allow-fallback=none disables all fallback for a mapping.
Python side: parse_options_for_map() in options/utils.py handles flag
parsing, ShortcutMapping uses it in __init__. get_shortcut() filters
candidates by per-mapping allow_fallback.
Go side: ParseMap() handles --allow-fallback, KeyAction stores
AllowFallback, ShortcutTracker.Match passes it to matching.
MatchesParsedShortcut defaults to shifted,ascii for hardcoded shortcuts.
Migrated kittens (themes, command_palette, diff, choose_files) to
use ShortcutTracker with configurable map entries.
Tests added for Python (5 test methods) and Go (ParseMap + key matching).