diff --git a/.Jules/palette.md b/.Jules/palette.md index 337e2dd..5f4e812 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -4,7 +4,7 @@ **Action:** Update `notice_line` in `src/shell/status_bar.rs` to include a visual indicator (e.g., prefix with an info/warning symbol like `i` or `!`, or Nerd Font icon if supported, but simpler is safer if we want it to work without Nerd Fonts. Actually, let's use standard unicode characters like `✓` for success/info and `⚠` for error). -## $(date +%Y-%m-%d) - Edit Mode Notice Consistency +## 2026-10-14 - Edit Mode Notice Consistency **Learning:** Edit mode specific notices (`EditState.notice`) previously bypassed the standard `notice_line` styling, resulting in warnings (unsaved changes) and errors (save failed) lacking visual distinction (color and icon) from normal hints. **Action:** Always ensure nested or state-specific notice mechanisms reuse the top-level standard styling to provide a consistent visual language across all modes. @@ -19,3 +19,6 @@ ## 2024-11-21 - Centering Empty States for TUI Lists **Learning:** Rendering an empty list message (like "no matches") in the top-left corner of the list area can look unbalanced, especially when rendering lists in large UI components or side panes. **Action:** Always center the text for empty state indicators in lists visually (using `.alignment(ratatui::layout::Alignment::Center)`) and vertically (using `center_text(msg, area.height)`) when using `Paragraph` to render empty states in a list widget. +## 2026-10-14 - Placeholder Text in Input Fields +**Learning:** Empty input fields (like the ones in Finder and Grep) lack visual guidance, making it unclear what the user is supposed to type or do, especially in a TUI environment. They just render a blank space or a lone cursor. +**Action:** When implementing or modifying search/filter input fields in TUI applications, always provide a descriptive placeholder text (e.g., "type to filter", "search workspace") styled distinctively (e.g., `Color::DarkGray`) when the query is empty. This improves discoverability. Ensure padding calculations account for the placeholder text width if the layout depends on the input's visual length. diff --git a/src/component/finder/view.rs b/src/component/finder/view.rs index 1563f76..63ed14c 100644 --- a/src/component/finder/view.rs +++ b/src/component/finder/view.rs @@ -36,17 +36,40 @@ pub(crate) fn draw_finder(frame: &mut Frame, finder: &mut Finder, scanning: bool fn draw_finder_input(frame: &mut Frame, finder: &Finder, area: Rect) { let query_text = format!("> {}", finder.query); let count = format!("{}/{}", finder.matches.len(), finder.total()); + + let mut spans = vec![Span::raw(query_text)]; + + // Placeholder text if query is empty + let placeholder = "type to filter"; + let is_empty = finder.query.is_empty(); + + // カーソル + spans.push(Span::styled( + " ", + Style::default().add_modifier(Modifier::REVERSED), + )); + + if is_empty { + spans.push(Span::styled( + placeholder, + Style::default().fg(Color::DarkGray), + )); + } + // 入力とカーソルの右側を件数表示までスペースで埋める。入力欄がそれより // 狭ければ埋めず単純に連結するだけにする (折り返しは Paragraph に任せる) - let used = query_text.chars().count() + 1 + count.chars().count(); + let base_used = finder.query.chars().count() + 2 + 1 + count.chars().count(); // "> " + query + cursor + count + let used = if is_empty { + base_used + placeholder.chars().count() + } else { + base_used + }; let pad = (area.width as usize).saturating_sub(used); - let line = Line::from(vec![ - Span::raw(query_text), - // 常に末尾に立つ簡易カーソル (他の入力行と同じ表現) - Span::styled(" ", Style::default().add_modifier(Modifier::REVERSED)), - Span::raw(" ".repeat(pad)), - Span::styled(count, Style::default().fg(Color::DarkGray)), - ]); + + spans.push(Span::raw(" ".repeat(pad))); + spans.push(Span::styled(count, Style::default().fg(Color::DarkGray))); + + let line = Line::from(spans); frame.render_widget(Paragraph::new(line), area); } diff --git a/src/component/grep/view.rs b/src/component/grep/view.rs index 765c75f..3ba96a8 100644 --- a/src/component/grep/view.rs +++ b/src/component/grep/view.rs @@ -53,11 +53,23 @@ fn title(grep: &GrepState) -> String { } fn draw_input(frame: &mut Frame, grep: &GrepState, area: Rect) { - let line = Line::from(vec![ - Span::raw(format!("> {}", grep.query)), - // 常に末尾に立つ簡易カーソル (Finder と同じ表現) - Span::styled(" ", Style::default().add_modifier(Modifier::REVERSED)), - ]); + let mut spans = vec![Span::raw(format!("> {}", grep.query))]; + + // 常に末尾に立つ簡易カーソル (Finder と同じ表現) + spans.push(Span::styled( + " ", + Style::default().add_modifier(Modifier::REVERSED), + )); + + // Placeholder text if query is empty + if grep.query.is_empty() { + spans.push(Span::styled( + "search workspace", + Style::default().fg(Color::DarkGray), + )); + } + + let line = Line::from(spans); frame.render_widget(Paragraph::new(line), area); }