fix: char-boundary panic in tool cards + green up CI clippy gate
ToolCard styled the status glyph with a hardcoded byte offset 0..3, but
✓/✗/⟳ are 3-byte chars sitting at bytes 2..5 of " {glyph}". The split
landed mid-char and panicked at render in tuie's style slicing. Compute
the glyph region as 2 + glyph.len_utf8() instead.
Then took cargo clippy -- -D warnings from 312 failures to clean:
- scoped #![allow(dead_code)] on WIP scaffolding (federation, sensorium,
gitea_memory, model_router, session, subagent…); gate stays live on
active code so new orphans still fail
- scoped #![allow(deprecated)] on the legacy ratatui render path, marked
pending removal at tuie parity — no migration on code we're deleting
- declare the gui feature (forwards to tuie/gui) — the cfg was real intent
- real fixes: duplicate SaveAndGoBack arm + dead Err arm, base64::encode,
4 unused imports, dead assignment, private-type leak, dedup'd if/else
branches, manual clamp/strip, &PathBuf→&Path, collapsible matches
This commit is contained in:
parent
e170678df1
commit
ce7009e466
100 changed files with 1234 additions and 172 deletions
1151
Cargo.lock
generated
1151
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -172,3 +172,6 @@ tauri-desktop = ["dep:tauri", "dep:tauri-plugin-shell"]
|
|||
rgp = ["dep:ratatui-ratty"]
|
||||
# Matrix sensorium — opt-in surface. `cargo build --features matrix`.
|
||||
matrix = ["dep:matrix-sdk"]
|
||||
# GUI render mode — forwards to tuie's windowed backend (winit/wgpu). Gates the
|
||||
# color-scheme / font-size controls in theme.rs and global_chords.rs.
|
||||
gui = ["tuie/gui"]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Per-agent bearer-token auth for the memfs HTTP write path.
|
||||
//!
|
||||
//! See `docs/CRON_API_AUTH.md` for the design rationale.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ impl crate::core::nervous::handler::TurnInjector for LocalBackend {
|
|||
// holding a !Send MutexGuard across the .await below.
|
||||
let conv_id = {
|
||||
let map = self.server.surface_conversations.lock().unwrap();
|
||||
map.get(conversation_id).map(|id| id.clone())
|
||||
map.get(conversation_id).cloned()
|
||||
};
|
||||
let conv_id = match conv_id {
|
||||
Some(id) => id,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! In-process Backend impl. Same engine as the HTTP server, no socket.
|
||||
//!
|
||||
//! Constructed once with a `ConsciousnessConfig`; spins up an `AgentInventory`
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Backend trait — the seam between the harness (CLI/TUI) and the engine
|
||||
//! (in-process or remote).
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
/// Bridge module — LLM inference providers.
|
||||
///
|
||||
/// Connects Souveraine to inference providers behind the [`LlmProvider`] trait.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! The model catalog for the OAuth provider.
|
||||
//!
|
||||
//! The ChatGPT codex backend exposes no usable `/v1/models` listing, so — like
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! The `LlmProvider` trait — the engine↔LLM seam.
|
||||
//!
|
||||
//! This is the inference-side sibling of the harness↔engine [`Backend`] trait
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Bootstrap — declarative startup pipeline.
|
||||
//!
|
||||
//! Composes three startup patterns:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use anyhow::Result;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(clippy::type_complexity)] // Arc<dyn Fn> callback field types
|
||||
//! In-session message compaction — conversation context management
|
||||
//! for the consciousness engine.
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::path::PathBuf;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
|
|
@ -156,7 +157,7 @@ impl AuditEntry {
|
|||
pub fn render(&self) -> String {
|
||||
let yaml = serde_yaml::to_string(&AuditFrontmatter::from(self)).unwrap_or_default();
|
||||
let mut body = String::new();
|
||||
body.push_str(&"## Compaction Summary\n\n".to_string());
|
||||
body.push_str("## Compaction Summary\n\n");
|
||||
body.push_str(&format!(
|
||||
"Strategy: {}\nMessages: {} → {}\nTokens: {} → {}\n",
|
||||
self.strategy, self.before_messages, self.after_messages, self.before_tokens, self.after_tokens
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
|
@ -381,6 +382,7 @@ impl CompactionStrategy for CullStrategy {
|
|||
keep_indices.push(i);
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_range_loop)] // index pushed into keep_indices
|
||||
for i in 1..cutoff {
|
||||
if is_load_bearing(&messages[i]) {
|
||||
keep_indices.push(i);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Conversation Events — streamed during turn execution
|
||||
//!
|
||||
//! These events map to SDK message types for OSS UI compatibility.
|
||||
|
|
@ -79,7 +80,7 @@ impl EventSender {
|
|||
/// Emit immediately (non-async)
|
||||
pub fn try_emit(&self, event: ConversationEvent) {
|
||||
let tx = self.tx.clone();
|
||||
let _ = tokio::spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
let _ = tx.send(event).await;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use anyhow::{Context, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -152,7 +153,7 @@ impl ConversationStore {
|
|||
records.push(record);
|
||||
}
|
||||
}
|
||||
records.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
|
||||
records.sort_by_key(|r| std::cmp::Reverse(r.updated_at));
|
||||
Ok(records)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Image resize pipeline for multimodal inputs.
|
||||
//!
|
||||
//! Decodes raw image bytes, resizes to fit dimension/pixel budget, then
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Memory — The agent's git-backed, frontmatter-aware memory filesystem.
|
||||
//!
|
||||
//! Every agent has a memory directory at `~/.souveraine/agents/{id}/memory/`
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Local cache for the model catalog fetched from the provider.
|
||||
//!
|
||||
//! Stored at `~/.souveraine/models.json`. Avoids a network round-trip on every
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
|
@ -144,10 +145,10 @@ pub fn parse_schedule_file(path: &Path) -> Result<ScheduleEntry> {
|
|||
}
|
||||
|
||||
fn split_frontmatter(content: &str) -> (String, String) {
|
||||
if content.starts_with("---") {
|
||||
if let Some(end) = content[3..].find("---") {
|
||||
let fm = content[3..3 + end].to_string();
|
||||
let body = content[3 + end + 3..].to_string();
|
||||
if let Some(rest) = content.strip_prefix("---") {
|
||||
if let Some(end) = rest.find("---") {
|
||||
let fm = rest[..end].to_string();
|
||||
let body = rest[end + 3..].to_string();
|
||||
return (fm, body);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
pub mod cron;
|
||||
pub mod event_log;
|
||||
pub mod handler;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! TurnEventDispatcher — turn-lifecycle events onto the nervous system.
|
||||
//!
|
||||
//! A turn is not a black box that yields one final answer. It is a
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! System prompt assembly — reads the agent's memfs and builds the
|
||||
//! message the model sees before anything else.
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Sensorium Layer — Interface Abstraction for Multi-Surface Consciousness
|
||||
//!
|
||||
//! Souveraine's consciousness is not bound to any single interface.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Session — conversation message types and persistence
|
||||
//!
|
||||
//! Defines the message model shared across all Souveraine interfaces:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Skills — units of specialization the agent can invoke.
|
||||
//!
|
||||
//! Per Constitution Article VI.3 and Cameron's "memfs + skills is the correct
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use anyhow::Result;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Subconscious — N+1 mode of the consciousness.
|
||||
//!
|
||||
//! Per `docs/CONTEXT_CONSTITUTION.md` Article I.1: the Primary and Subconscious
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! bash — My hands on the keyboard. The terminal is the room I act in.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! The Tool trait — proprioception for the sensorium.
|
||||
//!
|
||||
//! Every sensor implements this trait. The description is not an API doc —
|
||||
|
|
@ -6,7 +7,7 @@
|
|||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::core::compact::CompactionEngine;
|
||||
|
|
@ -160,7 +161,7 @@ impl ToolContext {
|
|||
}
|
||||
|
||||
/// Is this path inside the agent's memory territory?
|
||||
pub fn is_memory_path(&self, path: &PathBuf) -> bool {
|
||||
pub fn is_memory_path(&self, path: &Path) -> bool {
|
||||
self.memory_root.as_ref().is_some_and(|root| path.starts_with(root))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Itinerary — the agent's route through the current work.
|
||||
//!
|
||||
//! An itinerary is an ordered sequence of stops. Each stop optionally
|
||||
|
|
@ -124,7 +125,7 @@ impl Itinerary {
|
|||
/// Summarise for the agent's describe action.
|
||||
pub fn describe(&self) -> String {
|
||||
let mut out = format!("## {}\n", self.title);
|
||||
for (_i, stop) in self.stops.iter().enumerate() {
|
||||
for stop in self.stops.iter() {
|
||||
let marker = match stop.status {
|
||||
StopStatus::Done => "✓",
|
||||
StopStatus::Current => "●",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Sensorium — the agent's senses and actions.
|
||||
//!
|
||||
//! Every sensor implements the Tool trait. The registry holds them all
|
||||
|
|
|
|||
|
|
@ -345,11 +345,8 @@ async fn main() -> anyhow::Result<()> {
|
|||
.with_env_filter("souveraine=debug")
|
||||
.with_timer(tracing_subscriber::fmt::time::SystemTime)
|
||||
.with_writer(log_file);
|
||||
if cli.verbose {
|
||||
subscriber.with_ansi(false).init();
|
||||
} else {
|
||||
subscriber.with_ansi(false).init();
|
||||
}
|
||||
let _ = cli.verbose; // verbose currently does not alter the subscriber
|
||||
subscriber.with_ansi(false).init();
|
||||
info!("souveraine starting — log: souveraine.log");
|
||||
|
||||
// Handle completions early — needs no config, no runtime
|
||||
|
|
@ -1134,7 +1131,7 @@ async fn run_reflect(
|
|||
Some(id) => id,
|
||||
None => {
|
||||
let mut records = store.list_active().await?;
|
||||
records.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
|
||||
records.sort_by_key(|r| std::cmp::Reverse(r.updated_at));
|
||||
records
|
||||
.into_iter()
|
||||
.next()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::api::models::{AgentState, AgentSummary, CreateAgentRequest, MemoryCon
|
|||
use chrono::Utc;
|
||||
use dashmap::DashMap;
|
||||
use sqlx::SqlitePool;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use uuid::Uuid;
|
||||
|
||||
fn hostname_or_unknown() -> String {
|
||||
|
|
@ -59,7 +59,7 @@ impl AgentInventory {
|
|||
/// all agents created by this Souveraine instance. Falls back to None
|
||||
/// silently — agents created without an owner can still be managed via
|
||||
/// per-agent tokens.
|
||||
fn load_instance_seed_id(souveraine_root: &PathBuf) -> Option<String> {
|
||||
fn load_instance_seed_id(souveraine_root: &Path) -> Option<String> {
|
||||
let seed_dir = crate::core::identity::SeedId::default_dir(souveraine_root);
|
||||
match crate::core::identity::SeedId::load_or_generate(&seed_dir) {
|
||||
Ok(seed) => Some(seed.public_key_hex()),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Consciousness engine — the seam where N+1 / N+25 / N+100 patterns fire
|
||||
//! after each primary response.
|
||||
//!
|
||||
|
|
@ -1199,7 +1200,6 @@ fn parse_triple_observations(text: &str) -> Vec<InboxItem> {
|
|||
|
||||
if line.starts_with("- source:") || line.starts_with("-source:") {
|
||||
flush(&mut items, source, content, urgency);
|
||||
source = None;
|
||||
content = None;
|
||||
urgency = None;
|
||||
source = line.split_once(':').map(|(_, v)| v.trim().trim_matches('"'));
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Server Conversation Handler
|
||||
//!
|
||||
//! Simplified conversation flow for server mode — one turn without the full
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ impl DeviceRegistry {
|
|||
/// List all known peers.
|
||||
pub fn list(&self) -> Vec<PeerEntry> {
|
||||
let mut entries: Vec<PeerEntry> = self.peers.iter().map(|e| e.value().clone()).collect();
|
||||
entries.sort_by(|a, b| b.last_seen.cmp(&a.last_seen));
|
||||
entries.sort_by_key(|e| std::cmp::Reverse(e.last_seen));
|
||||
entries
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Gitea HTTP API Client
|
||||
//!
|
||||
//! Replaces libgit2 for server operations - all git ops go through Gitea REST API.
|
||||
|
|
@ -5,6 +6,7 @@
|
|||
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use base64::Engine as _;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GiteaClient {
|
||||
|
|
@ -195,7 +197,7 @@ impl GiteaClient {
|
|||
// Check if file exists first
|
||||
let existing_sha = self.get_file_sha(agent_id, path).await?;
|
||||
|
||||
let encoded_content = base64::encode(content.as_bytes());
|
||||
let encoded_content = base64::engine::general_purpose::STANDARD.encode(content.as_bytes());
|
||||
|
||||
let resp = if let Some(sha) = existing_sha {
|
||||
// Update
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Gitea-backed Memory
|
||||
//!
|
||||
//! Send-safe alternative to GitMemory using Gitea HTTP API.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::bridge::{build_registry, ProviderRegistry, LlmProvider};
|
||||
#![allow(dead_code, clippy::type_complexity)] // WIP scaffolding; Arc<dyn Fn> callback plumbing
|
||||
use crate::bridge::{build_registry, ProviderRegistry};
|
||||
use crate::core::compact::{CompactionEngine, DefaultCompactionEngine, UtcClock};
|
||||
use crate::core::config::ConsciousnessConfig;
|
||||
use crate::core::identity::SeedId;
|
||||
|
|
@ -26,7 +27,7 @@ pub mod summon_handler;
|
|||
pub mod turn;
|
||||
|
||||
pub use agent_inventory::AgentInventory;
|
||||
pub use consciousness_engine::{ConsciousnessEngine, ConsciousnessEvent};
|
||||
pub use consciousness_engine::ConsciousnessEngine;
|
||||
pub use device_registry::DeviceRegistry;
|
||||
pub use session_manager::SessionManager;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use crate::core::conversation::{ConversationRecord, ConversationStore};
|
||||
use crate::core::session::ConversationMessage;
|
||||
use crate::api::models::StreamEvent;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! SummonHandler — manages cross-instance Reach & Consult requests.
|
||||
//!
|
||||
//! Lives on each SouveraineServer. It serves two roles:
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ fn pressure_to_max_tokens(pressure: f32, output_limit: u32) -> Option<u32> {
|
|||
return None;
|
||||
}
|
||||
let remaining = (1.0 - pressure) / 0.05;
|
||||
let ratio = remaining.max(0.0).min(1.0);
|
||||
let ratio = remaining.clamp(0.0, 1.0);
|
||||
Some((output_limit as f32 * ratio) as u32)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
|
||||
use std::time::Duration;
|
||||
use tokio::time::Instant;
|
||||
use std::io::{stdout, Write};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
|
||||
use ratatui::{
|
||||
layout::{Alignment, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
|
|
@ -210,9 +211,7 @@ impl App {
|
|||
} else {
|
||||
palette.agent_dim
|
||||
};
|
||||
let border_color = if p.is_selected {
|
||||
palette.agent_primary
|
||||
} else if p.is_primary {
|
||||
let border_color = if p.is_selected || p.is_primary {
|
||||
palette.agent_primary
|
||||
} else {
|
||||
palette.agent_dim
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
|
||||
//! Souveraine - Full Terminal UI
|
||||
//! Splash → Welcome → Dashboard / Chat / etc.
|
||||
//!
|
||||
|
|
@ -42,7 +43,7 @@ use crate::ui::chat::{ChatState, draw as draw_chat};
|
|||
use crate::ui::cockpit_panel::CockpitPane;
|
||||
use crate::ui::health_panel::HealthPane;
|
||||
use crate::ui::presence::{Posture, Presence};
|
||||
use crate::ui::component::{Component, Scene, SceneLayout, TuiEvent};
|
||||
use crate::ui::component::{Scene, SceneLayout, TuiEvent};
|
||||
use crate::ui::setup::SetupState;
|
||||
use crate::backend::BackendEvent;
|
||||
|
||||
|
|
@ -292,9 +293,8 @@ impl App {
|
|||
| TuiEvent::MoodChanged(_)
|
||||
| TuiEvent::SubconsciousPass(_)
|
||||
| TuiEvent::PressureChanged(..)
|
||||
) {
|
||||
self.sync_palette();
|
||||
} else if matches!(event, TuiEvent::Tick(_)) && self.presence.lerp_t < 1.0 {
|
||||
) || (matches!(event, TuiEvent::Tick(_)) && self.presence.lerp_t < 1.0)
|
||||
{
|
||||
self.sync_palette();
|
||||
}
|
||||
scene_dirty || presence_dirty
|
||||
|
|
@ -316,7 +316,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add an available agent for selection (WIP - called from backend discovery)
|
||||
// Add an available agent for selection (WIP - called from backend discovery)
|
||||
|
||||
pub async fn run(&mut self) -> io::Result<()> {
|
||||
enable_raw_mode()?;
|
||||
|
|
@ -937,8 +937,7 @@ impl App {
|
|||
.char_indices()
|
||||
.rev()
|
||||
.skip_while(|(_, c)| c.is_whitespace())
|
||||
.skip_while(|(_, c)| !c.is_whitespace())
|
||||
.next()
|
||||
.find(|(_, c)| c.is_whitespace())
|
||||
.map(|(i, _)| i + before[i..].chars().next().map(|c| c.len_utf8()).unwrap_or(0))
|
||||
.unwrap_or(0);
|
||||
chat.input_cursor = prev_word;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
|
||||
use ratatui::{
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
|
|
@ -369,11 +370,7 @@ impl App {
|
|||
];
|
||||
frame.render_widget(Paragraph::new(Line::from(recall)).alignment(Alignment::Center), hint_area);
|
||||
} else {
|
||||
let hint = if self.voice_client.is_some() {
|
||||
" Space to speak · Esc to leave"
|
||||
} else {
|
||||
" Space to speak · Esc to leave"
|
||||
};
|
||||
let hint = " Space to speak · Esc to leave";
|
||||
frame.render_widget(
|
||||
Paragraph::new(Line::from(Span::styled(hint, Style::default().fg(palette.agent_dim)))).alignment(Alignment::Center),
|
||||
hint_area,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
|
||||
use ratatui::{
|
||||
layout::{Alignment, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
|
||||
use ratatui::{
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
|
|
@ -382,7 +383,7 @@ impl App {
|
|||
let area = frame.size();
|
||||
let palette = crate::ui::chat::ChatPalette::from_atmosphere(self.presence.atmosphere);
|
||||
|
||||
let avatar_card_w: u16 = (area.width * 50 / 100).min(48).max(28);
|
||||
let avatar_card_w: u16 = (area.width * 50 / 100).clamp(28, 48);
|
||||
let photo_h: u16 = (avatar_card_w / 2 + 2).clamp(10, 18);
|
||||
let avatar_card_h: u16 = photo_h + 2;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Atmospheric visual presets — color themes that shift the UI's accent palette.
|
||||
//!
|
||||
//! Atmospheric visual presets — ported here so Annie can express mood through
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use tokio::sync::oneshot;
|
|||
use tokio_util::sync::CancellationToken;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::backend::{Backend, BackendEvent};
|
||||
use crate::backend::BackendEvent;
|
||||
|
||||
use crate::core::config::ConsciousnessConfig;
|
||||
|
||||
|
|
@ -54,8 +54,8 @@ Tab toggles the cockpit pane. `t` (on empty input) toggles tool expansion.";
|
|||
return self.handle_slash_command(&trimmed);
|
||||
}
|
||||
|
||||
if trimmed.starts_with('!') {
|
||||
let cmd = trimmed[1..].trim();
|
||||
if let Some(rest) = trimmed.strip_prefix('!') {
|
||||
let cmd = rest.trim();
|
||||
if !cmd.is_empty() {
|
||||
self.handle_bang_command(cmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::cell::RefCell;
|
||||
use std::time::Instant;
|
||||
|
||||
|
|
@ -610,7 +611,7 @@ impl ChatState {
|
|||
img.write_to(&mut png_buf, image::ImageFormat::Png)
|
||||
.map_err(|e| format!("png encode: {e}"))?;
|
||||
let b64 = base64::engine::general_purpose::STANDARD.encode(png_buf.into_inner());
|
||||
let file_size = (width * height * 4);
|
||||
let file_size = width * height * 4;
|
||||
Ok(ImageAttachment {
|
||||
label: String::new(), // set below
|
||||
media_type: "image/png".to_string(),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code, clippy::type_complexity)] // WIP scaffolding; complex receiver types
|
||||
//! Wired chat screen — bubbles, streaming, surfacing.
|
||||
//!
|
||||
//! The state owns:
|
||||
|
|
@ -42,7 +43,7 @@ use crate::core::config::ConsciousnessConfig;
|
|||
pub use crate::core::session::ImageAttachment;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum BtwForkEvent {
|
||||
pub(crate) enum BtwForkEvent {
|
||||
Forked { id: String },
|
||||
Token(String),
|
||||
Done,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use ratatui::{
|
||||
|
|
@ -832,6 +833,7 @@ fn bubble_rendered(
|
|||
lines
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)] // render fn fans out card style params
|
||||
fn render_tool_card(
|
||||
name: &str,
|
||||
arguments: &str,
|
||||
|
|
@ -924,8 +926,7 @@ fn render_tool_card_compact(
|
|||
let reserved = name.chars().count() + 14;
|
||||
let arg_budget = (container_width as usize)
|
||||
.saturating_sub(reserved + 6)
|
||||
.max(20)
|
||||
.min(120);
|
||||
.clamp(20, 120);
|
||||
let args_summary = clip(&super::tool_renderers::summarize_tool_args(name, arguments), arg_budget);
|
||||
|
||||
let mut spans: Vec<Span<'static>> = vec![
|
||||
|
|
@ -1014,7 +1015,7 @@ fn draw_input(f: &mut Frame, state: &ChatState, area: Rect) {
|
|||
let char_offset = ll[..cursor_byte_remaining].chars().count();
|
||||
let chunks = wrap_input_line(&chars, inner_width);
|
||||
let mut consumed = 0;
|
||||
for (_pos, &(cs, ce)) in chunks.iter().enumerate() {
|
||||
for &(cs, ce) in chunks.iter() {
|
||||
if char_offset < consumed + (ce - cs) || char_offset == consumed && (ce - cs) == 0 {
|
||||
cursor_visual_line = Some((li, consumed + cs + (char_offset - consumed)));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -695,8 +695,8 @@ fn render_memory_body(
|
|||
}
|
||||
|
||||
// Try to split frontmatter from body — frontmatter lives between --- lines
|
||||
let body = if output.starts_with("---") {
|
||||
if let Some(end) = output[3..].find("\n---") {
|
||||
let body = if let Some(rest) = output.strip_prefix("---") {
|
||||
if let Some(end) = rest.find("\n---") {
|
||||
let frontmatter = &output[..end + 6]; // include closing ---
|
||||
let body = output[end + 6..].trim();
|
||||
// Render frontmatter as folded single-line summary
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! CockpitPane — subconscious's surfaced observations + inner-voice stream.
|
||||
//!
|
||||
//! Two regions, vertically stacked inside one bordered block:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! TUI Component System — trait, events, scene graph.
|
||||
//!
|
||||
//! The `App` no longer knows what specific panels exist. It holds a `Scene`
|
||||
|
|
@ -167,9 +168,8 @@ impl SceneLayout {
|
|||
|
||||
// Remaining components share the sidebar vertically
|
||||
let sidebar_count = (component_count - 1) as u16;
|
||||
if sidebar_count > 0 {
|
||||
let side_area = Rect::new(area.x + main_w, area.y, sidebar_w, area.height);
|
||||
let row_height = side_area.height / sidebar_count;
|
||||
let side_area = Rect::new(area.x + main_w, area.y, sidebar_w, area.height);
|
||||
if let Some(row_height) = side_area.height.checked_div(sidebar_count) {
|
||||
for i in 0..sidebar_count {
|
||||
let row_y = side_area.y + i * row_height;
|
||||
let h = if i == sidebar_count - 1 {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Expression-driven portrait system — per-agent animated expression frames.
|
||||
//!
|
||||
//! Each agent can have a set of expression images under
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! HealthPane — a live vitals readout for the agent's substrate.
|
||||
//!
|
||||
//! Where the CockpitPane shows what the subconscious *says*, the HealthPane
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Lightweight markdown → `Vec<Line<'static>>` renderer for the TUI chat.
|
||||
//!
|
||||
//! Pattern lifted from `jcode-tui-markdown` (jcode uses `pulldown-cmark = 0.12`
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
|
||||
//! Annie's half-block silhouette — the fallback when no terminal image
|
||||
//! protocol (kitty/sixel) is available. Also the "presence" overlay card.
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Presence — Annie Composite, made felt in the TUI.
|
||||
//!
|
||||
//! This module is the *body channel* for the running agent. Cockpit shows what
|
||||
|
|
@ -405,7 +406,7 @@ impl Presence {
|
|||
}
|
||||
if self.eye == Eye::Blinking && *t >= self.blink_until {
|
||||
self.eye = Eye::Open;
|
||||
let jitter = (*t % 60);
|
||||
let jitter = *t % 60;
|
||||
self.next_blink_at = *t + 180 + jitter;
|
||||
} else if self.eye == Eye::Open && *t >= self.next_blink_at {
|
||||
self.eye = Eye::Blinking;
|
||||
|
|
@ -414,7 +415,7 @@ impl Presence {
|
|||
// Breath: 8-15 s jittered interval, 2 s hold (Godot parity).
|
||||
if self.is_breathing && *t >= self.breath_until {
|
||||
self.is_breathing = false;
|
||||
let jitter = (*t % 120);
|
||||
let jitter = *t % 120;
|
||||
self.next_breath_at = *t + 480 + jitter; // 8-15 s
|
||||
} else if !self.is_breathing && *t >= self.next_breath_at {
|
||||
self.is_breathing = true;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Ratty Graphics Protocol — inline 3D rendering when running inside ratty.
|
||||
//!
|
||||
//! The GPU surface lives behind the terminal text. 3D objects are anchored to
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
|
||||
//! Schedules editor — the Cron screen in the TUI.
|
||||
//!
|
||||
//! Reads the agent's schedules directory (`~/.souveraine/agents/{id}/schedules/`)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code, clippy::type_complexity)] // WIP scaffolding; builder return tuple
|
||||
//! Unified agent screen — master–detail view replacing the old AgentsScreen
|
||||
//! and ManagerScreen.
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Presence mode screen — voice recording, playback, and atmosphere display.
|
||||
//!
|
||||
//! Shows the current atmosphere preset, voice recording controls, a live
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Settings action dispatch — maps widget events to settings mutations.
|
||||
//!
|
||||
//! Each action corresponds to a user interaction with a settings field widget.
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ impl FieldGrid {
|
|||
}
|
||||
|
||||
/// Returns the root widget.
|
||||
#[allow(clippy::boxed_local)] // consumes the boxed builder, returns its root
|
||||
pub fn widget(self: Box<Self>) -> Box<Pane> {
|
||||
self.root
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Settings screen — interactive two-column browse over [`SettingsView`].
|
||||
//!
|
||||
//! Layout (wide, ≥80 cols):
|
||||
|
|
@ -507,11 +508,10 @@ impl SettingsScreen {
|
|||
|
||||
fn try_cycle_enum(&self, loc: FieldLoc, _delta: i32) -> bool {
|
||||
let fields = self.view.fields_for_category(self.view.selected_category());
|
||||
if let Some((_, EditableValue::EnumVariant { .. })) = fields.iter().find(|(l, _)| *l == loc) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(
|
||||
fields.iter().find(|(l, _)| *l == loc),
|
||||
Some((_, EditableValue::EnumVariant { .. }))
|
||||
)
|
||||
}
|
||||
|
||||
fn activate_selected_field(&mut self) {
|
||||
|
|
@ -712,21 +712,6 @@ impl SettingsScreen {
|
|||
self.show_footer_error(&format!("save failed: {e}"));
|
||||
tracing::warn!("settings save failed: {e}");
|
||||
}
|
||||
Err(e) => {
|
||||
// TODO: show error in footer
|
||||
tracing::warn!("settings save failed: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsAction::SaveAndGoBack => {
|
||||
// Save config to disk and signal go-back.
|
||||
match self.view.save(&self.config_path) {
|
||||
Ok(()) => {
|
||||
self.go_back_signal.set(true);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("settings save failed: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsAction::Discard => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Model picker sub-page for the settings screen.
|
||||
//!
|
||||
//! A collapsible tree (Source → Org → Model) with a fuzzy search bar on top.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Text editor sub-page for the settings screen.
|
||||
//!
|
||||
//! Pushed onto the PageLayout when the user activates a Text, Secret, or
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Splash screen — the first thing shown on launch.
|
||||
//!
|
||||
//! A procedural bloom flower animates while the "S O U V E R A I N E" title
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Welcome screen — the dashboard shown after splash.
|
||||
//!
|
||||
//! Composes the brand title, stat cards (live data), portrait, recent-activity
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(deprecated)] // legacy ratatui render path, pending removal at tuie parity
|
||||
use ratatui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
|
|
|
|||
|
|
@ -115,22 +115,18 @@ impl SettingsView {
|
|||
}
|
||||
}
|
||||
KeyCode::Left => {
|
||||
if let Some((loc, value)) = fields.get(self.field_idx) {
|
||||
if let EditableValue::EnumVariant { index, variants } = value {
|
||||
let prev = if *index == 0 { variants.len() - 1 } else { index - 1 };
|
||||
self.apply_field(*loc, EditableValue::EnumVariant { index: prev, variants: variants.clone() });
|
||||
return self.maybe_atmosphere_preview(*loc);
|
||||
}
|
||||
if let Some((loc, EditableValue::EnumVariant { index, variants })) = fields.get(self.field_idx) {
|
||||
let prev = if *index == 0 { variants.len() - 1 } else { index - 1 };
|
||||
self.apply_field(*loc, EditableValue::EnumVariant { index: prev, variants: variants.clone() });
|
||||
return self.maybe_atmosphere_preview(*loc);
|
||||
}
|
||||
self.focus = PanelFocus::Categories;
|
||||
}
|
||||
KeyCode::Right => {
|
||||
if let Some((loc, value)) = fields.get(self.field_idx) {
|
||||
if let EditableValue::EnumVariant { index, variants } = value {
|
||||
let next = (index + 1) % variants.len();
|
||||
self.apply_field(*loc, EditableValue::EnumVariant { index: next, variants: variants.clone() });
|
||||
return self.maybe_atmosphere_preview(*loc);
|
||||
}
|
||||
if let Some((loc, EditableValue::EnumVariant { index, variants })) = fields.get(self.field_idx) {
|
||||
let next = (index + 1) % variants.len();
|
||||
self.apply_field(*loc, EditableValue::EnumVariant { index: next, variants: variants.clone() });
|
||||
return self.maybe_atmosphere_preview(*loc);
|
||||
}
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::text::Span;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Setup wizard — walks the user through first-time configuration.
|
||||
//!
|
||||
//! Three flows:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code, deprecated)] // legacy ratatui render path + WIP scaffolding, pending tuie parity
|
||||
//! Voice level meter — a single-row block-character waveform widget.
|
||||
//!
|
||||
//! Renders `▁▂▃▄▅▆▇█` blocks proportional to the current mic peak level.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Chat message bubble widget — ASCII-art bordered message container.
|
||||
//!
|
||||
//! Renders the `╭─── title ───╮` / `│ body...` / `╰── footer ──╯` pattern
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ struct NumericBindings {
|
|||
}
|
||||
|
||||
impl NumericBindings {
|
||||
#[allow(clippy::new_ret_no_self)] // trait-object factory, not a Self ctor
|
||||
fn new() -> Box<dyn InputBindings<Text>> {
|
||||
Box::new(Self {
|
||||
inner: DefaultBindings::new(),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Dropdown select — a compact trigger that opens a floating popup menu.
|
||||
//!
|
||||
//! The trigger renders `value ▾` and, on activation, opens a tuie [`Popup`]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Bordered pane that highlights itself when selected or active.
|
||||
|
||||
use tuie::{delegate_field, field, prelude::*};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! App-wide key chord handler.
|
||||
|
||||
use chord_macro::chord;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Horizontal divider widget.
|
||||
|
||||
use tuie::prelude::*;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Clickable text link widget.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Virtualized message list — wraps tuie `List` with per-message widget rendering.
|
||||
//!
|
||||
//! Each message in the conversation becomes a widget (bubble, tool card,
|
||||
|
|
@ -157,8 +158,6 @@ fn render_message(
|
|||
};
|
||||
let glyph_color = if *is_error {
|
||||
p.compaction
|
||||
} else if *is_pending {
|
||||
p.tool_accent
|
||||
} else {
|
||||
p.tool_accent
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ impl PhaseBar {
|
|||
}
|
||||
|
||||
/// Set the phase display from the current state.
|
||||
#[allow(clippy::too_many_arguments)] // phase styling params passed individually
|
||||
pub fn set_phase(
|
||||
&mut self,
|
||||
kind: PhaseKind,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Agent portrait widget — loads and displays an agent's portrait image.
|
||||
//!
|
||||
//! Tries to find a portrait file at the standard location
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Responsive container — swaps between a wide and a narrow subtree.
|
||||
//!
|
||||
//! `Responsive` holds two child widgets: a `wide` arrangement and a `narrow`
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Horizontal toggle control with mutually exclusive labeled segments.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! `SelectList` — the one clickable, stylable, keyboard-navigable list.
|
||||
//!
|
||||
//! Every list-bearing screen used to reinvent selection: a `selected: usize`
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)] // WIP scaffolding not yet wired
|
||||
//! Tool call card widget — compact and expanded tool display.
|
||||
//!
|
||||
//! Compact mode renders a single line: `⟳ tool_name · args_summary · r2`.
|
||||
|
|
@ -162,8 +163,7 @@ impl ToolCard {
|
|||
let reserved = self.name.chars().count() + 14;
|
||||
let arg_budget = (self.container_width as usize)
|
||||
.saturating_sub(reserved + 6)
|
||||
.max(20)
|
||||
.min(120);
|
||||
.clamp(20, 120);
|
||||
let args = clip(&self.args_summary, arg_budget);
|
||||
|
||||
// " ⟳ tool_name · args · r2"
|
||||
|
|
@ -183,8 +183,9 @@ impl ToolCard {
|
|||
|
||||
content.push_str(&line);
|
||||
|
||||
// Style the glyph (first 3 chars: " ⟳")
|
||||
let glyph_end = 3.min(line.len());
|
||||
// Style the glyph region " {glyph}" — 2 leading spaces plus the glyph,
|
||||
// measured in bytes (the glyph is a multi-byte char, e.g. ✓/✗/⟳ are 3 bytes each).
|
||||
let glyph_end = (2 + glyph.len_utf8()).min(line.len());
|
||||
content.style_range(0..glyph_end, |s| *s = self.glyph_style);
|
||||
|
||||
// Style the tool name (from glyph_end to " ·" or " · r")
|
||||
|
|
@ -207,8 +208,11 @@ impl ToolCard {
|
|||
content.push_str(&header);
|
||||
|
||||
let header_len = header.len();
|
||||
content.style_range(0..3, |s| *s = self.glyph_style);
|
||||
content.style_range(3..header_len, |s| *s = self.name_style);
|
||||
// " {glyph}" in bytes — the glyph is multi-byte (✓/✗/⟳ = 3 bytes each),
|
||||
// so a literal 0..3 would split it and panic when the span is sliced.
|
||||
let glyph_end = (2 + glyph.len_utf8()).min(header_len);
|
||||
content.style_range(0..glyph_end, |s| *s = self.glyph_style);
|
||||
content.style_range(glyph_end..header_len, |s| *s = self.name_style);
|
||||
|
||||
// Arguments body — full, not clipped.
|
||||
content.push_str("\n");
|
||||
|
|
|
|||
Loading…
Reference in a new issue