Skip to content

Phase 3: Add chord/macro/sequence suppression to keyboard overlay #77

@malpern

Description

@malpern

Centralized suppression plan (authoritative)

This issue is the authoritative source for finishing suppression in the keyboard overlay. The doc docs/central-suppression.md should mirror this issue, not diverge.

Goal

When Kanata transforms input (remaps, tap-hold, chords, macros, sequences), the overlay should highlight only the physical keys pressed, not the synthetic outputs.

Status (as of January 4, 2026)

Done:

  • Unified suppression decision (shouldSuppressKeyHighlight) used by CGEvent, TCP KeyInput, and flagsChanged.
  • A->B suppression centralized; delayed outputs handled with a small grace window (recentRemapSourceKeyCodes, 150ms).
  • Tap-hold suppression centralized; delayed outputs handled via recentTapOutputs (150ms).
  • TCP fallback indicator added to overlay header (shows when CGEvent input is active but TCP is missing).
  • Diagnostics flag added: KEYBOARD_SUPPRESSION_DEBUG_ENABLED.
  • Unit tests cover TCP fallback activation/clearing.

Phase 3 scope (not started)

Add suppression for chords, tap-dance, macros, and sequences.

Constraints

  • Do not remove CGEvent tap. It is the fallback pipeline.
  • TCP KeyInput is authoritative for physical input when available.
  • Suppression should remain data-driven (computed sets) and feed into the single unified check.
  • Keep suppression windows small and explicit (re-use 150ms windows unless justified).

Dependencies

  • Kanata emission events (some already defined in TCP protocol, but not all emitted yet):
    • ChordResolved
    • TapDanceResolved
    • OneShotActivated (if needed)
    • KeyOutput (optional, useful for macros/sequences)

Proposed suppression sets

Add computed sets and integrate into shouldSuppressKeyHighlight:

  • suppressedChordOutputKeyCodes (from ChordResolved events)
  • suppressedTapDanceOutputKeyCodes (from TapDanceResolved events)
  • suppressedMacroOutputKeyCodes (from KeyOutput stream or action-resolution events)
  • suppressedSequenceOutputKeyCodes (same as macro, or derived from resolved sequence events)

If Kanata doesn’t emit resolved events for a behavior, suppression should not guess. Instead, log a warning in debug mode and leave output visible until the proper event is available.

Acceptance criteria

  • Chord: When S+D -> Esc, only S and D highlight; Esc does not.
  • Tap-dance: When Q double-tap resolves to Tab, only Q highlights; Tab does not.
  • Macro/sequence: When a macro types “hello”, only the physical trigger key highlights; the output letters do not.
  • Existing remap/tap-hold behavior is unchanged and remains correct.
  • TCP fallback indicator remains correct (no false positives while TCP input is flowing).

Testing checklist

  • Chord: map S+D -> Esc, press chord; only S+D highlight.
  • Tap-dance: double-tap key; only physical key highlights.
  • Macro: trigger macro typing multiple keys; only physical trigger highlights.
  • Sequence: input sequence resolves; only physical keys highlight.
  • Regression: A->B still suppresses output B.
  • Regression: tap-hold still suppresses output key.
  • Regression: modifier remap (A->Shift) suppresses Shift highlight.

References

  • docs/central-suppression.md
  • Sources/KeyPathAppKit/UI/KeyboardVisualization/KeyboardVisualizationViewModel.swift
  • docs/kanata-fork/tcp-overlay-events.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions