#4083·arkime

Keyboard navigation

Author: 31453Created Jun 30, 2026Updated Jun 30, 2026
Labelsenhancementviewerparliamentwisecont3xt

Comprehensive keyboard shortcuts and navigation across the app — move between session rows, open/close detail, trigger common actions, jump between pages — built on a central, scoped, registry-driven shortcut system that auto-generates a searchable help cheat-sheet.

Part of the Arkime 7 UI epic (#3926). Large. Related prior art: #951 (closed a11y spec — the source of the in-view nav wishlist), #841 (original global-nav shortcuts), #2271 (cont3xt counterpart — will adopt this framework later).


Decisions (settled)

Area Decision
Activation Hybrid. Shift+letter = global nav/focus (today's model, kept). Bare keys (j/k, arrows, Enter, x…) = in-view item navigation. Bare keys fire only when focus is not in an input/select/textarea.
Architecture Central registry/service. Views register shortcuts (keys, scope, descKey, handler); the help UI is generated from the registry; descriptions are i18n'd; scope-awareness keeps Sessions keys from firing on other views. Replaces the App.vue switch + hardcoded help slot.
v1 coverage Sessions-focused — full Sessions in-view nav + polished global nav + generated help. SPIView / SPIGraph / Connections / Files deep nav deferred to follow-ups.
Reach Framework in common/vueapp, viewer wired in v1. Cont3xt (#2271), parliament, WISE adopt the same framework later with their own maps.
Nav keys Both j/k and / move focus (rows, and later sections).
Discoverability Full searchable cheat-sheet modal opened with ? — categorized (Global / Sessions / Detail), with a filter box. Replaces the current slide-in panel.
Customization Fixed default map for v1 (no remap UI). Registry keeps future user-remapping cheap to add.

What exists today (we build on / refactor this)

  • common/vueapp/KeyboardShortcuts.vue — Shift-hold detection (shiftKeyHold), the slide-in ? overlay, esc/outside-click close. Shared; also used by cont3xt.
  • viewer/vueapp/src/App.vue — one big keyup switch mapping Shift+letter → route/focus, plus the help text hardcoded in English in a template slot.
  • Current global map (Shift held): Q focus query · T focus time · S/A/V/G/C/H/U jump to Sessions/Arkime/SPIView/SPIGraph/Connections/Help/Hunt · Shift+Enter search · ? help · Esc blur/close.

Gap: everything today is global nav/focus. There is no in-view navigation (no focused-row concept, no keyboard detail toggle, no section/page nav). The Sessions table expands detail inline on click; pagination is the shared Vuetify Pagination.vue with no key support.


Architecture — central registry in common/

A single keyboard service replaces the scattered window listeners and the App.vue switch.

javascript
// common/vueapp/keyboard/registry (singleton + composable)
register({
  keys: ['j', 'ArrowDown'],
  scope: 'sessions',                  // 'global' | view name
  descKey: 'shortcuts.sessions.nextRow',
  handler: nextRow,
  when: () => rowsExist,              // optional guard
});
  • One global keydown/keyup listener in the service. Input-guard: bare keys never fire while focus is in input/select/textarea; Shift+letter guarded as today.
  • Scope resolution: active scope derives from the current route (+ optionally the focused component). Global shortcuts always live; view shortcuts only when their scope is active.
  • Shift-hold stays the trigger for the ? affordance and all Shift+letter global keys (reuse current detection, moved into the service).
  • Help generation: the cheat-sheet modal reads the registry, groups by scope, renders i18n descriptions → never drifts.
  • Migration: refactor KeyboardShortcuts.vue to be registry-driven; port the App.vue switch into global-scope registrations; delete the hardcoded help slot. Cont3xt must keep working — either leave it on the current minimal path until it adopts the framework, or migrate it as part of #2271 (coordinate; this is a shared-file refactor).

The shortcut map

Global (Shift held) — retained from today

Shift+Q focus query · Shift+T focus time · Shift+S/A/V/G/C/H/U jump to pages · Shift+Enter run search · Shift+P focus page picker (new, from #951) · ? help · Esc blur/close.

Sessions — in-view (bare keys, only when not in an input)

Keys Action
j / k / / focus next / prev session row
Enter open / close detail for the focused row
Esc close detail / clear row focus (layers under the app-wide blur)
x toggle-select the focused row (bulk actions)
a open the focused row's actions menu
O open / close all rows' detail (existing openAll/closeAll)
] / [ jump to next / prev meta section in the open detail (HTTP, DNS…)
n / p next / prev page

Conflict check (the ticket's third open question — resolved)

  • c stays global Connections under Shift; bare c is unused in-view → no clash.
  • Bare a/O/x/n/p/j/k are distinct from their Shift+letter global counterparts (different modifier state) → no clash.
  • Bare Enter (open detail) vs Shift+Enter (global search) → distinct.
  • Esc behavior layers: app-wide input-blur first; if a row is focused with no input active, also close detail / clear focus.

Implementation reality — Sessions focused-row mechanics

New work on the Sessions table (the bulk of v1):

  • A focusedIndex state + visible focus ring, scroll-into-view, and roving-tabindex for a11y.
  • Enter toggles the existing inline detail for the focused row; O reuses existing open/close-all.
  • a opens the existing per-row actions menu programmatically.
  • ] / [ requires SessionDetail.vue to expose ordered section anchors to navigate between.
  • n / p + Shift+P requires adding key hooks / a focus target to the shared Pagination.vue.

Components (new / changed)

  • common/vueapp/keyboard/ (new) — registry singleton + useKeyboardShortcuts composable + the single global listener.
  • common/vueapp/ShortcutsCheatSheet.vue (new) — searchable, categorized ? modal generated from the registry.
  • common/vueapp/KeyboardShortcuts.vue (refactor) — becomes registry-driven; shift detection delegated to the service; slide-in replaced by the cheat-sheet modal.
  • viewer/.../App.vue (refactor) — remove the switch + hardcoded help; register global shortcuts via the service.
  • viewer/.../sessions/Sessions.vue (change) — focused-row state, focus ring, scroll-into-view, register Sessions-scope shortcuts.
  • viewer/.../sessions/SessionDetail.vue (change) — expose section anchors for ] / [.
  • common/vueapp/Pagination.vue (change) — n/p/Shift+P hooks.
  • i18nshortcuts.* description keys across common/vueapp/locales/* (11 languages).

Phasing

  1. Registry + service in common; port today's global map into it; no behavior change for users yet.
  2. Cheat-sheet modal generated from the registry; retire the hardcoded slide-in help.
  3. Sessions focused-row foundation — focusedIndex, focus ring, scroll-into-view, a11y roving tabindex.
  4. Sessions actions — Enter/Esc detail, x select, a actions menu, O all, ]/[ sections.
  5. Pagination keys + Shift+P.
  6. Polish — a11y pass, i18n strings, conflict QA, UI review.

Out of scope for v1

  • In-view nav for SPIView (header navigate/expand/collapse), SPIGraph (key-box focus), Connections (graph pan/zoom/node-walk), Files (pagination) — follow-ups per #951.
  • User remapping / a Shortcuts settings tab.
  • Cont3xt (#2271) / parliament / WISE wiring — they adopt the common framework later.

Remaining open questions (small)

  • Scope resolution source of truth: route-only vs. route + focused-component (matters once a view has multiple focusable regions).
  • Esc layering exact order when both an input is focused and a detail row is open.
  • Whether ? should also be reachable without holding Shift (i.e. bare ? = Shift+/ already) — confirm the key event we bind.
  • Focus-ring visual treatment + whether the focused row should also become the keyboard target for native Tab.