Enhance tool documentation and add new features
- Added module-level documentation for memory tools (`remember`, `recall`, `forget`) to clarify their purpose. - Improved documentation in `recall.rs` and `remember.rs` to describe the functionality and flow of memory entry operations. - Updated `mod.rs` to include descriptions for the tool trait and execution context. - Enhanced `plan.rs` with detailed comments on plan-mode signaling tools. - Documented text search tools in `search.rs` to explain their functionality. - Improved sequential-thinking tool documentation in `seqthink.rs`. - Added safety filter documentation in `shell_filter` for credential and git operations. - Enhanced utility tools documentation, including `cd`, `dir_cache_update`, and `todowrite`. - Improved rendering documentation in view modules (`chat`, `markdown`, `status`, `workflow`) to clarify rendering flows and purposes.
This commit is contained in:
@@ -1,5 +1,16 @@
|
||||
//! Bash mode: handles submitting a shell command from the bash input panel.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
|
||||
/// Launch a background bash job for the submitted command.
|
||||
///
|
||||
/// Flow: ignore empty input → spawn the job (fire-and-forget, the job's
|
||||
/// output is polled elsewhere via `bgbash::control`) → mark state dirty
|
||||
/// so the TUI re-renders.
|
||||
///
|
||||
/// Why: the returned `BashJob` handle is intentionally dropped — this
|
||||
/// function only needs to kick the job off; the job registers itself in
|
||||
/// the shared jobs map for later polling.
|
||||
pub fn handle_bash_submit(state: &mut AppStateRest, command: String) {
|
||||
if !command.is_empty() {
|
||||
let _ = crate::app::bgbash::job::spawn_bash_job(command);
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
//! Editor mode: a minimal in-TUI line editor for viewing/modifying a file,
|
||||
//! with bounded undo history.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use crate::app::state::types::Overlay;
|
||||
|
||||
/// State for the built-in line editor overlay: buffer contents, cursor
|
||||
/// position, and a bounded undo stack.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EditorState {
|
||||
pub path: String,
|
||||
@@ -23,6 +28,8 @@ impl Default for EditorState {
|
||||
}
|
||||
|
||||
impl EditorState {
|
||||
/// Create a fresh editor state for `path`, seeded with existing content
|
||||
/// (or a single empty line for a new file).
|
||||
pub fn open(path: String, existing_content: Option<Vec<String>>) -> Self {
|
||||
let content = existing_content.unwrap_or_else(|| vec![String::new()]);
|
||||
EditorState {
|
||||
@@ -32,12 +39,20 @@ impl EditorState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new empty line immediately after the cursor line.
|
||||
///
|
||||
/// Why: snapshots content to the undo stack first, matching every other
|
||||
/// mutating method here.
|
||||
pub fn insert_line_after(&mut self) {
|
||||
self.save_undo();
|
||||
let pos = (self.cursor_line + 1).min(self.content.len());
|
||||
self.content.insert(pos, String::new());
|
||||
}
|
||||
|
||||
/// Push a snapshot of the current content onto the undo stack, capped at 50 entries.
|
||||
///
|
||||
/// Why: `remove(0)` on overflow bounds memory use at the cost of O(n)
|
||||
/// shifting; the cap (50) keeps that cost negligible in practice.
|
||||
fn save_undo(&mut self) {
|
||||
self.undo_stack.push(self.content.clone());
|
||||
if self.undo_stack.len() > 50 {
|
||||
@@ -45,6 +60,7 @@ impl EditorState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the cursor down one line, clamping the column to the new line's length.
|
||||
pub fn cursor_down(&mut self) {
|
||||
if self.cursor_line + 1 < self.content.len() {
|
||||
self.cursor_line += 1;
|
||||
@@ -54,6 +70,7 @@ impl EditorState {
|
||||
);
|
||||
}
|
||||
|
||||
/// Insert a character at the cursor and advance the cursor past it.
|
||||
pub fn insert_char(&mut self, c: char) {
|
||||
self.save_undo();
|
||||
if let Some(line) = self.content.get_mut(self.cursor_line) {
|
||||
@@ -62,6 +79,11 @@ impl EditorState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete the character before the cursor (backspace).
|
||||
///
|
||||
/// Flow: if not at column 0, remove the preceding char on this line →
|
||||
/// otherwise (start of line, not the first line) merge this line into
|
||||
/// the previous one, joining at the old line's end.
|
||||
pub fn delete_left(&mut self) {
|
||||
self.save_undo();
|
||||
if let Some(line) = self.content.get_mut(self.cursor_line) {
|
||||
@@ -78,11 +100,18 @@ impl EditorState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Join all lines with `\n` into the full file contents, for saving.
|
||||
pub fn as_string(&self) -> String {
|
||||
self.content.join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
/// Feed a chunk of typed text into the active editor, translating newlines
|
||||
/// and tabs into editor operations.
|
||||
///
|
||||
/// Flow: no-op if no editor is open → for each char: `\n`/`\r` inserts a
|
||||
/// line and moves down, `\t` inserts two spaces, everything else inserts
|
||||
/// the char directly → mark state dirty.
|
||||
pub fn handle_editor_input(state: &mut AppStateRest, text: String) {
|
||||
let editor = &mut state.misc.editor;
|
||||
if editor.is_none() {
|
||||
@@ -108,6 +137,7 @@ pub fn handle_editor_input(state: &mut AppStateRest, text: String) {
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
/// Close the editor overlay without saving, clearing editor state.
|
||||
pub fn handle_editor_dismiss(state: &mut AppStateRest) {
|
||||
state.misc.editor = None;
|
||||
state.misc.overlay = Overlay::None;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! Effort mode: cycles the agent's reasoning effort level, which scales the
|
||||
//! LLM's temperature and max_tokens for subsequent turns.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
|
||||
pub const EFFORT_LEVELS: &[&str] = &["low", "medium", "high", "xhigh", "max"];
|
||||
@@ -17,15 +20,24 @@ pub fn generation_params(level: usize, base_max_tokens: u32) -> (f32, u32) {
|
||||
(temperature, max_tokens.max(256))
|
||||
}
|
||||
|
||||
/// Return the current effort level index, clamped to a valid `EFFORT_LEVELS` slot.
|
||||
///
|
||||
/// Why: clamping guards against a stale/out-of-range value in loaded state
|
||||
/// (e.g. after `EFFORT_LEVELS` shrinks between versions).
|
||||
pub fn current_effort(state: &AppStateRest) -> usize {
|
||||
state.misc.effort_level.min(EFFORT_LEVELS.len() - 1)
|
||||
}
|
||||
|
||||
/// Return the current effort level's display name (e.g. "medium").
|
||||
pub fn current_effort_str(state: &AppStateRest) -> &'static str {
|
||||
let idx = current_effort(state);
|
||||
EFFORT_LEVELS[idx]
|
||||
}
|
||||
|
||||
/// Advance to the next effort level, wrapping around, and toast the new value.
|
||||
///
|
||||
/// Flow: compute `(current + 1) % len` → store it → push an info toast with
|
||||
/// the new level's label → mark state dirty.
|
||||
pub fn cycle_effort(state: &mut AppStateRest) {
|
||||
let current = current_effort(state);
|
||||
state.misc.effort_level = (current + 1) % EFFORT_LEVELS.len();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Help mode: static help text and the action that opens/closes the help overlay.
|
||||
|
||||
use crate::app::runtime::actions::Action;
|
||||
use crate::app::state::types::Overlay;
|
||||
|
||||
@@ -27,6 +29,12 @@ Slash commands:
|
||||
/lesson import Import lessons
|
||||
/clear Clear transcript";
|
||||
|
||||
/// Route an incoming action while the help overlay is open.
|
||||
///
|
||||
/// Flow: `CloseOverlay` passes through unchanged; any other action is
|
||||
/// treated as "open help" (idempotent — re-opens the overlay it's already on).
|
||||
///
|
||||
/// Return: the `Action` to actually dispatch.
|
||||
pub fn handle_help_action(action: &Action) -> Action {
|
||||
match action {
|
||||
Action::CloseOverlay => Action::CloseOverlay,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//! Key input mode: raw text capture overlay used for one-off key/text prompts.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
|
||||
/// Replace the input buffer with the given text and mark state dirty.
|
||||
pub fn handle_key_text(state: &mut AppStateRest, text: String) {
|
||||
state.input.buffer = text;
|
||||
state.dirty = true;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Loading mode: transient overlay shown while waiting on an async operation.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
|
||||
pub const LOADING_MESSAGES: &[&str] = &[
|
||||
@@ -7,6 +9,7 @@ pub const LOADING_MESSAGES: &[&str] = &[
|
||||
"almost done...",
|
||||
];
|
||||
|
||||
/// Mark state dirty to force a re-render (e.g. to advance the loading spinner/message).
|
||||
pub fn resolve_loading(state: &mut AppStateRest) {
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
//! MCP mode: overlay for connecting to a configured MCP server.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
|
||||
/// Placeholder entry point for connecting to an MCP server by name.
|
||||
///
|
||||
/// Why: not yet wired to `McpManager::connect_stdio` — currently just
|
||||
/// marks state dirty so the overlay re-renders.
|
||||
pub fn connect_mcp(state: &mut AppStateRest, server_name: &str) {
|
||||
let _ = server_name;
|
||||
state.dirty = true;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! TUI mode definitions and per-mode input/action handlers, one submodule
|
||||
//! per overlay/mode (bash, editor, effort, mcp, quit confirm, rewind, etc.).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod bash;
|
||||
@@ -11,6 +14,8 @@ pub mod rewind;
|
||||
pub mod settings;
|
||||
pub mod todo;
|
||||
|
||||
/// Which input/overlay mode the TUI is currently in; drives both key
|
||||
/// routing (`controller/input.rs`) and rendering.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ModeKind {
|
||||
Chat,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
//! Quit-confirm mode: the "are you sure?" overlay shown before exiting.
|
||||
|
||||
use crate::app::runtime::actions::Action;
|
||||
|
||||
/// Translate the user's yes/no answer on the quit-confirm overlay into an action.
|
||||
///
|
||||
/// Return: `Action::ForceQuit` if confirmed, otherwise `Action::CloseOverlay`
|
||||
/// to dismiss the prompt without quitting.
|
||||
pub fn handle_quit_confirm(yes: bool) -> Action {
|
||||
if yes {
|
||||
Action::ForceQuit
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! Rewind mode: restores a file to a pre-edit snapshot stored in the
|
||||
//! session's SQLite blob store.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use sha2::Digest;
|
||||
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
//! Settings-mode helper logic for the TUI settings overlay.
|
||||
//!
|
||||
//! Flow: exposes small mutation functions (currently just cycling the
|
||||
//! internet access mode) invoked by keybindings while the settings overlay
|
||||
//! is active.
|
||||
|
||||
use crate::model::settings::{Settings, InternetMode};
|
||||
|
||||
/// Advance the internet access mode to the next value in the cycle.
|
||||
///
|
||||
/// Flow: Off -> ReadOnly -> Full -> Off, wrapping around.
|
||||
///
|
||||
/// Why: used by a settings-toggle keybinding to step through modes
|
||||
/// without needing a dropdown/menu.
|
||||
///
|
||||
/// Return: nothing; mutates `settings.internet_mode` in place.
|
||||
pub fn cycle_internet_mode(settings: &mut Settings) {
|
||||
settings.internet_mode = match settings.internet_mode {
|
||||
InternetMode::Off => InternetMode::ReadOnly,
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
//! Todo-mode helper logic for the TUI todo-list overlay.
|
||||
//!
|
||||
//! Flow: exposes the toggle handler invoked by a keybinding to show/hide
|
||||
//! the todo overlay.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use crate::app::state::types::Overlay;
|
||||
|
||||
/// Toggle the todo-list overlay open or closed.
|
||||
///
|
||||
/// Flow: if the todo overlay is currently shown, hide it (set to `Overlay::None`);
|
||||
/// otherwise show it.
|
||||
///
|
||||
/// Why: marks state dirty so the TUI re-renders on the next frame.
|
||||
///
|
||||
/// Return: nothing; mutates `state.misc.overlay` and `state.dirty` in place.
|
||||
pub fn handle_todo_toggle(state: &mut AppStateRest) {
|
||||
if state.misc.overlay == Overlay::Todo {
|
||||
state.misc.overlay = Overlay::None;
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
//! Workflow-mode helper logic for the TUI workflow overlay.
|
||||
//!
|
||||
//! Flow: exposes a dismiss handler invoked by a keybinding to close the
|
||||
//! workflow overlay, and a status query used elsewhere to check whether
|
||||
//! it is currently showing.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use crate::app::state::types::Overlay;
|
||||
|
||||
/// Close the workflow overlay if it is the currently active overlay.
|
||||
///
|
||||
/// Flow: check `state.misc.overlay == Overlay::Workflow`, reset to `Overlay::None`
|
||||
/// if so, then mark state dirty regardless.
|
||||
///
|
||||
/// Why: no-ops safely if another overlay is showing, so it can be called
|
||||
/// unconditionally from a dismiss keybinding.
|
||||
///
|
||||
/// Return: nothing; mutates `state.misc.overlay` and `state.dirty` in place.
|
||||
pub fn handle_workflow_dismiss(state: &mut AppStateRest) {
|
||||
if state.misc.overlay == Overlay::Workflow {
|
||||
state.misc.overlay = Overlay::None;
|
||||
@@ -8,6 +23,11 @@ pub fn handle_workflow_dismiss(state: &mut AppStateRest) {
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
/// Report whether the workflow overlay is currently displayed.
|
||||
///
|
||||
/// Flow: compare `state.misc.overlay` against `Overlay::Workflow`.
|
||||
///
|
||||
/// Return: `"active"` if the workflow overlay is shown, `"idle"` otherwise.
|
||||
pub fn workflow_status(state: &AppStateRest) -> &str {
|
||||
if state.misc.overlay == Overlay::Workflow {
|
||||
"active"
|
||||
|
||||
Reference in New Issue
Block a user