feat: add context-window meter and in-conversation search to TUI

クローズ
#1,357 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

関連するプルリクエストがすでにマージされています。

  • #1363 @avoidwork による — マージ済み

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
22/100
issue の種類
機能追加
明瞭さ
おおむね明確
活発さ
活発
技術スタック
javascript
領域
cli

調査の方向性

Start with the context-window meter in src/tui/statusBar.js, where contextSize is currently rendered via formatSize in the showContext block, and reuse getContextUtilizationColor for the ratio. Search (Ctrl+F) spans src/tui/app.js, messageList.js and scrollView.js, and the issue itself recommends splitting it out, so confirm scope with the maintainer first. Done means the meter renders at 0%, 50%, 100% and falls back when contextWindow is unset, with tests passing via npm run test.

索引モデルが issue の本文から書いたものです。

説明

feature

Summary

Add a context-window meter to the TUI status bar (replacing the bare contextSize number with a visual bar) and add in-conversation search (Ctrl+F) with match highlighting and jump-to-next.

Motivation

The status bar currently shows a bare contextSize number, which is not intuitive — users cannot tell at a glance how much of the context window is consumed. Long conversations are also hard to navigate; in-conversation search lets users filter and jump to specific messages without scrolling through the whole transcript.

Proposed Solution

  1. Context-window meter — Replace the bare contextSize number in src/tui/statusBar.js with a visual bar, e.g. [▮▮▮▯▯▯] 62%, showing the proportion of the context window used.
  2. In-conversation search — Bind Ctrl+F to filter/search messages, with match highlighting and jump-to-next. Builds on the virtualized src/tui/scrollView.js and src/tui/messageList.js.

Dependencies

  • No new runtime dependencies expected — this builds on the existing TUI rendering stack. If a search/filter helper is required, prefer a lightweight in-house implementation over adding a dependency. Confirm exact packages during implementation.

Testing Strategy

  • Unit tests: Verify the meter renders correctly at 0%, 50%, and 100% context usage; verify search/filter logic matches expected messages and highlights matches.
  • Integration test: Simulate Ctrl+F in the TUI, type a query, and verify matches are highlighted and jump-to-next advances through results.
  • Edge cases: Empty conversation, no matches found, very long messages, context at 0% and 100%, search query with special characters.

Security Considerations

  • Credential storage: Not applicable — no credentials involved.
  • Input validation: Validate and sanitize the search query before filtering; guard against regex injection if the query is used in a regex.
  • OWASP: No new attack surface beyond normal TUI input handling.

Alternatives Considered

  • Meter: Keep the bare number, or show only a percentage. A visual bar was chosen for at-a-glance readability.
  • Search: A separate search panel vs. inline filter. Inline filter with jump-to-next was chosen for minimal disruption to the existing scroll view.

Split Recommendation

This issue bundles multiple distinct capabilities. Consider splitting into separate issues:

  • Context-window meter — small, self-contained change to src/tui/statusBar.js.
  • In-conversation search — larger, touches src/tui/scrollView.js and src/tui/messageList.js, and requires keyboard handling.

MVP recommendation: Start with the context-window meter as it has the least dependencies and highest immediate value.

OpenSpec Note

This project uses OpenSpec for feature development. If this request is approved, I will:

  1. Run /opsx:propose to generate a full proposal with specs and tasks
  2. Iterate on the design before any code is written
  3. Follow the task-driven implementation workflow

Additional Context

Split out from issue 1339.

Audit Findings (for Issue #1357)

  • src/tui/statusBar.js — The contextSize value is rendered via formatSize(contextSize) in the showContext block (around line 117). The contextWindow prop is already available, and getContextUtilizationColor(contextSize, contextWindow) already computes the utilization percentage. The meter can reuse this: compute contextSize / contextWindow and render a bar like [▮▮▮▯▯▯] 62%. Handle the contextWindow <= 0 (unset) case — currently getContextUtilizationColor falls back to cyan; the meter should fall back to the bare number or a neutral display.
  • src/tui/scrollView.js — VirtualScrollView exposes an imperative API via ref: scrollTo, scrollBy, scrollToTop, scrollToBottom, getScrollOffset, getContentHeight, getViewportHeight, getBottomOffset, remeasure, remeasureItem. It renders only the visible window via computeVisibleRange(heights, viewportHeight, scrollOffset, overscan). For jump-to-next, compute the matched message's cumulative offset from the offsets array and call scrollTo(offset).
  • src/tui/messageList.js — MessageList builds renderData (filtered by shouldRenderBubble) and textOffsets for selection mapping. It exposes getMessages() returning {text, top} per message — search can reuse this to locate matches and compute the scroll target. The renderItem already accepts a selection prop (global char range mapped to a local bubble range) and passes it to MessageBubble — search match highlighting can reuse this selection mechanism.
  • src/tui/app.js — The global useInput handler (line 367) routes keys in conversation view. Ctrl+F is not currently handled; add a key.ctrl && input === "f" branch to toggle search mode. Note the existing focus-aware routing (tab toggles inputFocused), so search mode must coordinate with input focus.
  • src/tui/inputArea.js — Passes contextSize and contextWindow to StatusBar (lines 191-195). No change needed here for the meter; the data source already exists.

Fix Steps

  1. Add the context-window meter — In src/tui/statusBar.js, replace the formatSize(contextSize) render in the showContext block with a visual bar. Compute utilization = contextWindow > 0 ? contextSize / contextWindow : 0, render [▮▮▮▯▯▯] 62% using block characters, and color it via the existing getContextUtilizationColor. Fall back to the bare number when contextWindow <= 0.
  2. Add a search state to MessageList — In src/tui/messageList.js, add a searchQuery state and a searchIndex (current match). Add a setSearchQuery/clearSearch imperative API. Reuse getMessages() to find matches and compute the scroll target offset.
  3. Highlight matches — In src/tui/messageList.js renderItem, when a search query is active, compute the local match ranges within each bubble's text and pass them to MessageBubble (reuse the existing selection prop mechanism or add a highlights prop).
  4. Wire Ctrl+F — In src/tui/app.js, add a key.ctrl && input === "f" branch in the global useInput handler to toggle search mode and focus the search input. Coordinate with the existing inputFocused routing so search mode takes precedence.
  5. Add jump-to-next — In src/tui/messageList.js, add a searchNext/searchPrev imperative method that advances searchIndex and calls scrollRef.current.scrollTo(offset) using the matched message's cumulative offset.
  6. Write tests — Add unit tests for the meter (0%, 50%, 100%, and unset contextWindow) and for search matching/highlighting/jump-to-next. Add an integration test simulating Ctrl+F. Run npm run test, npm run lint, and npm run coverage.
主要言語
JavaScript
スター
3
フォーク
0
平均マージ
1時間 49分
マージ済み PR(30日)
224

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

avoidwork/madz のほかの issue

avoidwork/madz の issue をすべて見る

似ている issue

JavaScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。