Skip to content

Vim navigation refactor: declarative "navigable elements" (epic) #104

Description

@binarypath

Stub. Plan in detail when we get to it.

Problem

Vim navigation is per-page. Every view (info, ideas, economics, news cards, sketch metrics, AI trading panels, financials rows, SEC filings rows, sector items …) has its own j / k / h / l / Enter handler hand-coded in terminal.js. Each handler knows about the specific DOM structure it operates on. Side-effects:

  • New pages start broken. The crypto / forex / index / ETF stubs from Security types — foundation (routes + dispatch + legacy redirect) #98 ship with no tab navigation — pressing j/k does nothing because none of the existing per-page handlers match. The user has to mouse to switch tabs until each type's vim handler is bolted on.
  • Adding a new tab or section means editing both the page's render code AND the corresponding handler block in terminal.js. Easy to forget; easy to drift.
  • Cross-page nav semantics differ (e.g. j in news cards advances cards; j in AI tab enters content focus; j in financials tab enters rows). Inconsistent friction.

Insert mode (Esc) and search (s) work fine — they're global. Navigation should be too.

What we want

A first-class navigable element concept. Any element in any template can opt in by declaring itself navigable (e.g. data-vim="item" or a CSS class). The shared vim core handles:

  • j / k move focus to the next / previous navigable element in DOM order.
  • h / l move across a sibling group (tabs row, sub-tabs row, column grids) — defined by a navigable element being a "group".
  • Enter triggers the element's primary action (defined by data-vim-action="…" or by being a link / button).
  • Selected state via a single shared .vim-selected CSS class — no more per-page selection classes.
  • Scoping: a "focused container" (the current view's content area) is the default search root. Nested navigable groups can be entered/exited with j/k boundaries.

Page authors should add nav by adding attributes to the template, not by writing a JS handler.

Why this matters beyond consistency

This is a prerequisite for #94 (per-paragraph p → pop chart) and the broader "pin a paragraph + chart to an idea" goal. Once paragraphs are first-class navigable elements, pinning, sharing, and attachment all become annotations on the same primitive.

It also lets the new type-specific pages from #97 (crypto / forex / index / etf) ship navigation for free instead of duplicating the info-tab handlers four times.

Out of scope (for the eventual plan)

  • Not a complete rewrite of terminal.js — keep the global key dispatcher and the search bar / insert-mode plumbing as-is.
  • Not a swap to a framework. Vanilla JS, in-place refactor.

Pre-work / context

  • Current per-page handlers live in internal/server/static/terminal.js (see vimHandlers.{ideas, economics, watchlist, graph, news, info} and info._focus = main|sub|content).
  • Recent work on the AI tab (feat: multi-agent trading analysis pipeline (phase 1) #39, fix(info): vim flow into Run Deep Analysis button #92) already uses a data-trading-vim attribute pattern — a small precedent for declarative nav.
  • The financials tab (_finGetRows), SEC tab (_secGetRows), trading-panels (_tradingVimHandler) all expose the same shape (rows + selected idx + enter handler) through window.* globals — strong signal these should unify.

When this gets picked up, plan it as its own epic with discrete migration issues per existing view.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backlogItems migrated from ideas.md backlogfeature

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions