ADR-020: Scoped Shortcut Engine as Interaction Layer

Prev Next

Context

ONECMS required keyboard shortcuts (theme toggle, help dialog) and context-aware interaction support.

Existing behavior relied on module-level window.addEventListener usage, which introduced:

  • Scattered key handling
  • No scope control (modal vs page conflicts)
  • No permission or feature-flag filtering
  • No lifecycle safety guarantees
  • No unified action registry

Additionally, future requirements include:

  • Context-specific guided walkthroughs
  • Discoverability surfaces
  • Potential command palette expansion

A decision was required on whether to:

  • Use an existing shortcut/command library, or
  • Build a minimal internal interaction runtime.

Decision

We implemented a custom, dependency-free Scoped Shortcut Engine under src/lib/internal/ShortcutManager.

The engine provides:

  • Stack-based scoped resolution
  • Declarative registration via useShortcut
  • Policy injection (feature flags, permissions, editable guard)
  • Single global keyboard listener
  • Centralized action registry

No third-party shortcut or command palette packages were used.

The engine is intentionally UI-agnostic and serves as an interaction runtime, not merely a key detector.

Consequences

Enables

  • Deterministic scope priority (modal overrides page)
  • Context-aware guided walkthroughs
  • Future command palette without architectural changes
  • Permission- and feature-aware action filtering
  • Centralized discoverability surface
  • Strict testable infra (≥90% branch coverage)

Restricts

  • Internal ownership of keyboard edge-case handling
  • Responsibility for long-term maintenance
  • No out-of-the-box fuzzy search or UI primitives

Trade-offs Accepted

  • Slight initial implementation overhead
  • Custom combo matcher instead of library abstraction
  • Additional test surface

These trade-offs were accepted to maintain architectural control, extensibility, and consistency with existing runtime patterns (AI runtime, upload engine, reminder engine).

Notes

The global dialog acts as a shell and may evolve to render:

  • Context-specific walkthrough content
  • Shortcut list
  • Future command palette surface

No architectural changes are required to support these expansions.