mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:23:33 +00:00
feat(tui/history): route core cells via apply_event
This commit is contained in:
Generated
+1
@@ -1249,6 +1249,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"shlex",
|
||||
"strip-ansi-escapes",
|
||||
"strum 0.27.2",
|
||||
|
||||
@@ -64,6 +64,7 @@ reqwest = { version = "0.12", default-features = false, features = ["json", "rus
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1", features = ["preserve_order"] }
|
||||
shlex = "1.3.0"
|
||||
sha2 = { workspace = true }
|
||||
tempfile = "3"
|
||||
arboard = "3"
|
||||
strum = "0.27.2"
|
||||
|
||||
@@ -150,11 +150,17 @@ Status legend: ✅ complete (semantic deterministic state ready), ⏳ still need
|
||||
|
||||
## Step 3 – HistoryState Manager *(Pending)*
|
||||
3.1 **HistoryRecord enum** – ✅ complete: `history/state.rs` defines `HistoryRecord`, per-cell state structs, and `HistoryState` scaffolding with ID management helpers.
|
||||
3.2 **ChatWidget incremental adoption** – Introduce `HistoryState`/`HistoryRenderState` alongside the legacy `history_cells` vector, migrate low-risk cell types (plain, loading, wait status) to the new state, then remove the legacy vector once the path is stable. **Status:** assistant streaming/final answer flows now write into `HistoryState`; next migrate remaining cell constructors and have render adaptors consume the recorded state.
|
||||
3.3 **Apply-event pipeline** – Implement `HistoryState::apply_event(&mut self, event: &EventMsg)` covering all core/TUI event types (exec lifecycle, tool updates, background notices, resume snapshots, undo) and route migrated cells through it.
|
||||
3.2 **ChatWidget incremental adoption** – Introduce `HistoryState`/`HistoryRenderState` alongside the legacy `history_cells` vector, migrate low-risk cell types (plain, loading, wait status) to the new state, then remove the legacy vector once the path is stable. **Status:** assistant streaming/final answer flows now write into `HistoryState`; plain/background/wait/loading cells, running/completed tool calls, plan updates, upgrade notices, reasoning summaries, exec cells, diff summaries, patch summaries, and image previews now insert exclusively via `history_insert_with_key_global_tagged` + `HistoryState::apply_event`. Next steps: migrate explore aggregations, rate-limit notices, and remaining niche cells before deleting the legacy vector. **Update (2025-09-26):** migration will now happen only via the new `HistoryState::apply_event` entrypoint so every cell’s lifecycle is driven by events rather than bespoke insert/replace helpers.
|
||||
3.3 **Apply-event pipeline** – Implement `HistoryState::apply_event(&mut self, event: &EventMsg)` covering all core/TUI event types (exec lifecycle, tool updates, background notices, resume snapshots, undo) and route migrated cells through it. **Status:** Insert events now assign `HistoryId`s for plain/background/loading/wait/tool/plan/upgrade/reasoning/exec/diff/assistant/patch/image cells and keep `history_cell_ids` in sync; replacement/removal paths still mirror the legacy vector and need to be ported. Pending: wire explore/rate-limit mutations through dedicated `HistoryEvent` variants, propagate patch success/failure updates via `HistoryEvent::Replace`, and delete the remaining direct `history_cells` manipulations. **Update (2025-09-26):** build `apply_event` first, then iterate cell-by-cell: for each event family (plain messages, loading indicators, wait status, etc.) add a handler in `apply_event`, update `ChatWidget` to publish only events, and adapt rendering to rebuild from the resulting `HistoryRecord`s. The legacy `history_cells` vector will temporarily mirror `HistoryState` via an ID→cell cache until Step 3.5 removes it entirely.
|
||||
3.4 **Undo/resume hooks** – Expose `snapshot`, `restore`, and `truncate_after(id)` to support /undo and resume flows.
|
||||
3.5 **ChatWidget full integration** – Delete `history_cells: Vec<Box<dyn HistoryCell>>`, wire all helper methods (`history_push`, `history_replace`, etc.) into `HistoryState`, and treat Step 4 as the follow-up for centralized rendering.
|
||||
|
||||
### Outstanding Gaps (2025-09-26)
|
||||
- Explore aggregation cells still maintain bespoke `ExploreAggregationState`; convert into `ExploreRecord` instances so inserts/replays serialize cleanly.
|
||||
- Rate-limit warnings and compact panels render outside the history pipeline; decide whether they should emit `HistoryRecord::RateLimits` snapshots or remain overlay-only.
|
||||
- Patch apply success/failure currently mutates `PatchSummaryCell` in place; emit `HistoryEvent::Replace` once the replace arm is implemented so records stay authoritative.
|
||||
- `HistoryState::apply_event` only drives inserts today; removal/replace helpers still operate directly on `history_cells` and must migrate before we can drop the legacy vector.
|
||||
|
||||
## Step 4 – Event Mapping & Rendering *(Pending)*
|
||||
4.1 **Centralize handlers** – Route every mutation in `handle_*` (exec events, tool deltas, diff updates, background notices) through `HistoryState` so ordering/id management lives in one place.
|
||||
4.2 **Stable IDs for streaming** – Store `HistoryId` + domain IDs (exec id, tool call id, stream id) to ensure in-place updates and dedupe.
|
||||
|
||||
+281
-33
@@ -211,11 +211,14 @@ use crate::history_cell::HistoryCell;
|
||||
use crate::history_cell::HistoryCellType;
|
||||
use crate::history_cell::PatchEventType;
|
||||
use crate::history_cell::PlainHistoryCell;
|
||||
use crate::history::state::PatchEventType as HistoryPatchEventType;
|
||||
use crate::history::state::{
|
||||
AssistantMessageState,
|
||||
AssistantStreamDelta,
|
||||
AssistantStreamState,
|
||||
HistoryEvent,
|
||||
HistoryId,
|
||||
HistoryMutation,
|
||||
HistoryRecord,
|
||||
HistoryState,
|
||||
MessageMetadata,
|
||||
@@ -428,6 +431,7 @@ pub(crate) struct ChatWidget<'a> {
|
||||
login_add_view_state: Option<Weak<RefCell<LoginAddAccountState>>>,
|
||||
active_exec_cell: Option<ExecCell>,
|
||||
history_cells: Vec<Box<dyn HistoryCell>>, // Store all history in memory
|
||||
history_cell_ids: Vec<Option<HistoryId>>,
|
||||
history_render: HistoryRenderState,
|
||||
history_state: HistoryState,
|
||||
config: Config,
|
||||
@@ -1210,7 +1214,7 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
}
|
||||
let key = self.system_order_key(placement, order);
|
||||
let pos = self.history_insert_with_key_global_tagged(Box::new(cell), key, tag);
|
||||
let pos = self.history_insert_with_key_global_tagged(Box::new(cell), key, tag, None);
|
||||
if let Some(id) = id_for_replace {
|
||||
self.system_cell_by_id.insert(id, pos);
|
||||
}
|
||||
@@ -1567,7 +1571,7 @@ impl ChatWidget<'_> {
|
||||
use crate::history::state::ExecStatus;
|
||||
use crate::history_cell::HistoryCellType;
|
||||
use crate::history_cell::PatchKind;
|
||||
use crate::history_cell::ToolStatus;
|
||||
use crate::history_cell::ToolCellStatus;
|
||||
match item.kind() {
|
||||
HistoryCellType::Plain => "Plain".to_string(),
|
||||
HistoryCellType::User => "User".to_string(),
|
||||
@@ -1590,9 +1594,9 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
HistoryCellType::Tool { status } => {
|
||||
let s = match status {
|
||||
ToolStatus::Running => "Running",
|
||||
ToolStatus::Success => "Success",
|
||||
ToolStatus::Failed => "Failed",
|
||||
ToolCellStatus::Running => "Running",
|
||||
ToolCellStatus::Success => "Success",
|
||||
ToolCellStatus::Failed => "Failed",
|
||||
};
|
||||
format!("Tool:{}", s)
|
||||
}
|
||||
@@ -1738,9 +1742,10 @@ impl ChatWidget<'_> {
|
||||
let mut lines = Vec::new();
|
||||
crate::markdown::append_markdown(text, &mut lines, &self.config);
|
||||
let key = self.next_internal_key();
|
||||
let _ = self.history_insert_with_key_global(
|
||||
Box::new(PlainHistoryCell::new(lines, HistoryCellType::Assistant)),
|
||||
let _ = self.history_insert_plain_cell_with_key(
|
||||
PlainHistoryCell::new(lines, HistoryCellType::Assistant),
|
||||
key,
|
||||
"epilogue",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1761,6 +1766,7 @@ impl ChatWidget<'_> {
|
||||
Box::new(crate::history_cell::new_background_event(message)),
|
||||
key,
|
||||
"background",
|
||||
None,
|
||||
);
|
||||
}
|
||||
ResponseItem::Reasoning { summary, .. } => {
|
||||
@@ -1824,6 +1830,7 @@ impl ChatWidget<'_> {
|
||||
Box::new(crate::history_cell::new_background_event(message)),
|
||||
key,
|
||||
"background",
|
||||
None,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
@@ -2033,9 +2040,32 @@ impl ChatWidget<'_> {
|
||||
// invalidate the height cache since indices shift and our cache is
|
||||
// keyed by (idx,width).
|
||||
let before_len = self.history_cells.len();
|
||||
self.history_cells.retain(|cell| !cell.should_remove());
|
||||
if self.history_cells.len() != before_len {
|
||||
self.invalidate_height_cache();
|
||||
if before_len > 0 {
|
||||
let old_cells = std::mem::take(&mut self.history_cells);
|
||||
let old_ids = std::mem::take(&mut self.history_cell_ids);
|
||||
debug_assert_eq!(
|
||||
old_cells.len(),
|
||||
old_ids.len(),
|
||||
"history ids out of sync with cells"
|
||||
);
|
||||
let mut removed_any = false;
|
||||
let mut kept_cells = Vec::with_capacity(old_cells.len());
|
||||
let mut kept_ids = Vec::with_capacity(old_ids.len());
|
||||
for (cell, id) in old_cells.into_iter().zip(old_ids.into_iter()) {
|
||||
if cell.should_remove() {
|
||||
removed_any = true;
|
||||
continue;
|
||||
}
|
||||
kept_ids.push(id);
|
||||
kept_cells.push(cell);
|
||||
}
|
||||
self.history_cells = kept_cells;
|
||||
self.history_cell_ids = kept_ids;
|
||||
if removed_any {
|
||||
self.invalidate_height_cache();
|
||||
}
|
||||
} else if !self.history_cell_ids.is_empty() {
|
||||
self.history_cell_ids.clear();
|
||||
}
|
||||
|
||||
// Send a redraw event to trigger UI update
|
||||
@@ -2174,6 +2204,7 @@ impl ChatWidget<'_> {
|
||||
{
|
||||
summary.title = "Updated".to_string();
|
||||
summary.kind = history_cell::PatchKind::ApplySuccess;
|
||||
summary.record_mut().patch_type = HistoryPatchEventType::ApplySuccess;
|
||||
self.request_redraw();
|
||||
return;
|
||||
}
|
||||
@@ -2699,6 +2730,7 @@ impl ChatWidget<'_> {
|
||||
pending_upgrade_notice: None,
|
||||
history_render: HistoryRenderState::new(),
|
||||
history_state: HistoryState::new(),
|
||||
history_cell_ids: Vec::new(),
|
||||
height_manager: RefCell::new(HeightManager::new(
|
||||
crate::height_manager::HeightManagerConfig::default(),
|
||||
)),
|
||||
@@ -2932,6 +2964,7 @@ impl ChatWidget<'_> {
|
||||
pending_upgrade_notice: None,
|
||||
history_render: HistoryRenderState::new(),
|
||||
history_state: HistoryState::new(),
|
||||
history_cell_ids: Vec::new(),
|
||||
height_manager: RefCell::new(HeightManager::new(
|
||||
crate::height_manager::HeightManagerConfig::default(),
|
||||
)),
|
||||
@@ -4224,7 +4257,7 @@ impl ChatWidget<'_> {
|
||||
cell: Box<dyn HistoryCell>,
|
||||
key: OrderKey,
|
||||
) -> usize {
|
||||
self.history_insert_with_key_global_tagged(cell, key, "untagged")
|
||||
self.history_insert_with_key_global_tagged(cell, key, "untagged", None)
|
||||
}
|
||||
|
||||
// Internal: same as above but with a short tag for debug overlays.
|
||||
@@ -4233,6 +4266,7 @@ impl ChatWidget<'_> {
|
||||
cell: Box<dyn HistoryCell>,
|
||||
key: OrderKey,
|
||||
tag: &'static str,
|
||||
record: Option<HistoryRecord>,
|
||||
) -> usize {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
@@ -4340,7 +4374,27 @@ impl ChatWidget<'_> {
|
||||
None
|
||||
};
|
||||
|
||||
let mut cell = cell;
|
||||
|
||||
let record = record.or_else(|| self.history_record_from_cell(cell.as_ref()));
|
||||
|
||||
let maybe_id = if let Some(record) = record {
|
||||
match self
|
||||
.history_state
|
||||
.apply_event(HistoryEvent::Insert { index: pos, record })
|
||||
{
|
||||
HistoryMutation::Inserted { id, .. } => {
|
||||
self.assign_history_id(&mut cell, id);
|
||||
Some(id)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.history_cells.insert(pos, cell);
|
||||
self.history_cell_ids.insert(pos, maybe_id);
|
||||
// In terminal mode, App mirrors history lines into the native buffer.
|
||||
// Ensure order vector is also long enough for position after cell insert
|
||||
if self.cell_order_seq.len() < pos {
|
||||
@@ -4396,6 +4450,179 @@ impl ChatWidget<'_> {
|
||||
pos
|
||||
}
|
||||
|
||||
fn assign_history_id(&self, cell: &mut Box<dyn HistoryCell>, id: HistoryId) {
|
||||
if let Some(plain) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::PlainHistoryCell>()
|
||||
{
|
||||
plain.state_mut().id = id;
|
||||
} else if let Some(wait) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::WaitStatusCell>()
|
||||
{
|
||||
wait.state_mut().id = id;
|
||||
} else if let Some(loading) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::LoadingCell>()
|
||||
{
|
||||
loading.state_mut().id = id;
|
||||
} else if let Some(background) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::BackgroundEventCell>()
|
||||
{
|
||||
background.state_mut().id = id;
|
||||
} else if let Some(tool_call) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::ToolCallCell>()
|
||||
{
|
||||
tool_call.state_mut().id = id;
|
||||
} else if let Some(running_tool) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::RunningToolCallCell>()
|
||||
{
|
||||
running_tool.state_mut().id = id;
|
||||
} else if let Some(plan) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::PlanUpdateCell>()
|
||||
{
|
||||
plan.state_mut().id = id;
|
||||
} else if let Some(upgrade) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::UpgradeNoticeCell>()
|
||||
{
|
||||
upgrade.state_mut().id = id;
|
||||
} else if let Some(reasoning) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::CollapsibleReasoningCell>()
|
||||
{
|
||||
reasoning.set_history_id(id);
|
||||
} else if let Some(exec) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::ExecCell>()
|
||||
{
|
||||
exec.record.id = id;
|
||||
} else if let Some(stream) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::StreamingContentCell>()
|
||||
{
|
||||
stream.state_mut().id = id;
|
||||
} else if let Some(assistant) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::AssistantMarkdownCell>()
|
||||
{
|
||||
assistant.state_mut().id = id;
|
||||
} else if let Some(diff) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::DiffCell>()
|
||||
{
|
||||
diff.record_mut().id = id;
|
||||
} else if let Some(image) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::ImageOutputCell>()
|
||||
{
|
||||
image.record_mut().id = id;
|
||||
} else if let Some(patch) = cell
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::history_cell::PatchSummaryCell>()
|
||||
{
|
||||
patch.record_mut().id = id;
|
||||
}
|
||||
}
|
||||
|
||||
fn history_record_from_cell(&self, cell: &dyn HistoryCell) -> Option<HistoryRecord> {
|
||||
if let Some(plain) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::PlainHistoryCell>()
|
||||
{
|
||||
return Some(HistoryRecord::PlainMessage(plain.state().clone()));
|
||||
}
|
||||
if let Some(wait) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::WaitStatusCell>()
|
||||
{
|
||||
return Some(HistoryRecord::WaitStatus(wait.state().clone()));
|
||||
}
|
||||
if let Some(loading) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::LoadingCell>()
|
||||
{
|
||||
return Some(HistoryRecord::Loading(loading.state().clone()));
|
||||
}
|
||||
if let Some(background) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::BackgroundEventCell>()
|
||||
{
|
||||
return Some(HistoryRecord::BackgroundEvent(background.state().clone()));
|
||||
}
|
||||
if let Some(tool_call) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::ToolCallCell>()
|
||||
{
|
||||
return Some(HistoryRecord::ToolCall(tool_call.state().clone()));
|
||||
}
|
||||
if let Some(running_tool) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::RunningToolCallCell>()
|
||||
{
|
||||
return Some(HistoryRecord::RunningTool(running_tool.state().clone()));
|
||||
}
|
||||
if let Some(plan) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::PlanUpdateCell>()
|
||||
{
|
||||
return Some(HistoryRecord::PlanUpdate(plan.state().clone()));
|
||||
}
|
||||
if let Some(upgrade) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::UpgradeNoticeCell>()
|
||||
{
|
||||
return Some(HistoryRecord::UpgradeNotice(upgrade.state().clone()));
|
||||
}
|
||||
if let Some(reasoning) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::CollapsibleReasoningCell>()
|
||||
{
|
||||
return Some(HistoryRecord::Reasoning(reasoning.reasoning_state()));
|
||||
}
|
||||
if let Some(exec) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::ExecCell>()
|
||||
{
|
||||
return Some(HistoryRecord::Exec(exec.record.clone()));
|
||||
}
|
||||
if let Some(stream) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::StreamingContentCell>()
|
||||
{
|
||||
return Some(HistoryRecord::AssistantStream(stream.state().clone()));
|
||||
}
|
||||
if let Some(assistant) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::AssistantMarkdownCell>()
|
||||
{
|
||||
return Some(HistoryRecord::AssistantMessage(assistant.state().clone()));
|
||||
}
|
||||
if let Some(diff) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::DiffCell>()
|
||||
{
|
||||
return Some(HistoryRecord::Diff(diff.record().clone()));
|
||||
}
|
||||
if let Some(image) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::ImageOutputCell>()
|
||||
{
|
||||
return Some(HistoryRecord::Image(image.record().clone()));
|
||||
}
|
||||
if let Some(patch) = cell
|
||||
.as_any()
|
||||
.downcast_ref::<crate::history_cell::PatchSummaryCell>()
|
||||
{
|
||||
return Some(HistoryRecord::Patch(patch.record().clone()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Push a cell using a synthetic global order key at the bottom of the current request.
|
||||
pub(crate) fn history_push(&mut self, cell: impl HistoryCell + 'static) {
|
||||
#[cfg(debug_assertions)]
|
||||
@@ -4406,18 +4633,29 @@ impl ChatWidget<'_> {
|
||||
);
|
||||
}
|
||||
let key = self.next_internal_key();
|
||||
let _ = self.history_insert_with_key_global_tagged(Box::new(cell), key, "epilogue");
|
||||
let _ = self.history_insert_with_key_global_tagged(Box::new(cell), key, "epilogue", None);
|
||||
}
|
||||
|
||||
fn history_insert_plain_cell_with_key(
|
||||
&mut self,
|
||||
cell: crate::history_cell::PlainHistoryCell,
|
||||
key: OrderKey,
|
||||
tag: &'static str,
|
||||
) -> usize {
|
||||
let record = HistoryRecord::PlainMessage(cell.state().clone());
|
||||
self.history_insert_with_key_global_tagged(Box::new(cell), key, tag, Some(record))
|
||||
}
|
||||
|
||||
fn history_push_plain_cell(&mut self, cell: crate::history_cell::PlainHistoryCell) {
|
||||
let key = self.next_internal_key();
|
||||
let _ = self.history_insert_plain_cell_with_key(cell, key, "epilogue");
|
||||
}
|
||||
|
||||
fn history_push_diff(&mut self, title: Option<String>, diff_output: String) {
|
||||
let mut record = history_cell::diff_record_from_string(
|
||||
let record = history_cell::diff_record_from_string(
|
||||
title.unwrap_or_default(),
|
||||
&diff_output,
|
||||
);
|
||||
let id = self
|
||||
.history_state
|
||||
.push(HistoryRecord::Diff(record.clone()));
|
||||
record.id = id;
|
||||
let cell = history_cell::DiffCell::from_record(record);
|
||||
self.history_push(cell);
|
||||
}
|
||||
@@ -4468,17 +4706,20 @@ impl ChatWidget<'_> {
|
||||
/// Push a cell using a synthetic key at the TOP of the NEXT request.
|
||||
fn history_push_top_next_req(&mut self, cell: impl HistoryCell + 'static) {
|
||||
let key = self.next_req_key_top();
|
||||
let _ = self.history_insert_with_key_global_tagged(Box::new(cell), key, "prelude");
|
||||
let _ = self.history_insert_with_key_global_tagged(Box::new(cell), key, "prelude", None);
|
||||
}
|
||||
/// Push a user prompt so it appears right under banners and above model output for the next request.
|
||||
fn history_push_prompt_next_req(&mut self, cell: impl HistoryCell + 'static) {
|
||||
let key = self.next_req_key_prompt();
|
||||
let _ = self.history_insert_with_key_global_tagged(Box::new(cell), key, "prompt");
|
||||
let _ = self.history_insert_with_key_global_tagged(Box::new(cell), key, "prompt", None);
|
||||
}
|
||||
|
||||
fn history_replace_at(&mut self, idx: usize, cell: Box<dyn HistoryCell>) {
|
||||
if idx < self.history_cells.len() {
|
||||
self.history_cells[idx] = cell;
|
||||
if idx < self.history_cell_ids.len() {
|
||||
self.history_cell_ids[idx] = None;
|
||||
}
|
||||
self.invalidate_height_cache();
|
||||
self.request_redraw();
|
||||
self.refresh_explore_trailing_flags();
|
||||
@@ -4503,6 +4744,9 @@ impl ChatWidget<'_> {
|
||||
fn history_remove_at(&mut self, idx: usize) {
|
||||
if idx < self.history_cells.len() {
|
||||
self.history_cells.remove(idx);
|
||||
if idx < self.history_cell_ids.len() {
|
||||
self.history_cell_ids.remove(idx);
|
||||
}
|
||||
if idx < self.cell_order_seq.len() {
|
||||
self.cell_order_seq.remove(idx);
|
||||
}
|
||||
@@ -4762,7 +5006,7 @@ impl ChatWidget<'_> {
|
||||
"command: {}",
|
||||
original_text.trim()
|
||||
)));
|
||||
self.history_push(crate::history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(crate::history_cell::PlainHistoryCell::new(
|
||||
ack,
|
||||
crate::history_cell::HistoryCellType::Notice,
|
||||
));
|
||||
@@ -4817,7 +5061,7 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
)));
|
||||
lines.push(Line::from(format!("command: {}", original_text.trim())));
|
||||
self.history_push(crate::history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(crate::history_cell::PlainHistoryCell::new(
|
||||
lines,
|
||||
crate::history_cell::HistoryCellType::Notice,
|
||||
));
|
||||
@@ -6874,9 +7118,10 @@ impl ChatWidget<'_> {
|
||||
if let Some(idx) = resolved_idx {
|
||||
self.history_replace_at(idx, Box::new(wait_cancelled_cell));
|
||||
} else {
|
||||
let _ = self.history_insert_with_key_global(
|
||||
Box::new(wait_cancelled_cell),
|
||||
let _ = self.history_insert_plain_cell_with_key(
|
||||
wait_cancelled_cell,
|
||||
ok,
|
||||
"untagged",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7170,7 +7415,7 @@ impl ChatWidget<'_> {
|
||||
for line in prompt_text.lines() {
|
||||
lines.push(Line::from(line.to_string()));
|
||||
}
|
||||
self.history_push(history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(history_cell::PlainHistoryCell::new(
|
||||
lines,
|
||||
history_cell::HistoryCellType::Notice,
|
||||
));
|
||||
@@ -7543,7 +7788,7 @@ impl ChatWidget<'_> {
|
||||
for l in text.lines() {
|
||||
lines.push(ratatui::text::Line::from(l.to_string()))
|
||||
}
|
||||
self.history_push(crate::history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(crate::history_cell::PlainHistoryCell::new(
|
||||
lines,
|
||||
crate::history_cell::HistoryCellType::Notice,
|
||||
));
|
||||
@@ -7849,7 +8094,7 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
self.history_push(crate::history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(crate::history_cell::PlainHistoryCell::new(
|
||||
lines,
|
||||
crate::history_cell::HistoryCellType::Notice,
|
||||
));
|
||||
@@ -10442,7 +10687,7 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
// Insert new status near the top of this request window
|
||||
let key = self.near_time_key(None);
|
||||
let pos = self.history_insert_with_key_global_tagged(Box::new(cell), key, "background");
|
||||
let pos = self.history_insert_with_key_global_tagged(Box::new(cell), key, "background", None);
|
||||
self.access_status_idx = Some(pos);
|
||||
}
|
||||
|
||||
@@ -10672,7 +10917,10 @@ impl ChatWidget<'_> {
|
||||
pub(crate) fn undo_jump_back(&mut self) {
|
||||
if let Some(mut st) = self.pending_jump_back.take() {
|
||||
// Restore removed cells in original order
|
||||
self.history_cells.extend(st.removed_cells.drain(..));
|
||||
for cell in st.removed_cells.drain(..) {
|
||||
self.history_cell_ids.push(None);
|
||||
self.history_cells.push(cell);
|
||||
}
|
||||
// Clear composer (no reliable way to restore prior text)
|
||||
self.insert_str("");
|
||||
self.request_redraw();
|
||||
@@ -11699,7 +11947,7 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
|
||||
// Add status message
|
||||
self.history_push(history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(history_cell::PlainHistoryCell::new(
|
||||
vec![Line::from("✅ Chrome launched with user profile")],
|
||||
history_cell::HistoryCellType::BackgroundEvent,
|
||||
));
|
||||
@@ -12324,7 +12572,7 @@ impl ChatWidget<'_> {
|
||||
}
|
||||
|
||||
// Add status message
|
||||
self.history_push(history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(history_cell::PlainHistoryCell::new(
|
||||
vec![Line::from(format!(
|
||||
"✅ Chrome launched with temporary profile at {}",
|
||||
profile_dir.display()
|
||||
@@ -12433,7 +12681,7 @@ impl ChatWidget<'_> {
|
||||
|
||||
// Add status message
|
||||
let status_msg = format!("🌐 Opening internal browser: {}", full_url);
|
||||
self.history_push(history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(history_cell::PlainHistoryCell::new(
|
||||
vec![Line::from(status_msg)],
|
||||
history_cell::HistoryCellType::BackgroundEvent,
|
||||
));
|
||||
@@ -12831,7 +13079,7 @@ impl ChatWidget<'_> {
|
||||
.lines()
|
||||
.map(|line| Line::from(line.to_string()))
|
||||
.collect();
|
||||
self.history_push(history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(history_cell::PlainHistoryCell::new(
|
||||
lines,
|
||||
history_cell::HistoryCellType::BackgroundEvent,
|
||||
));
|
||||
@@ -13627,7 +13875,7 @@ impl ChatWidget<'_> {
|
||||
.lines()
|
||||
.map(|line| Line::from(line.to_string()))
|
||||
.collect();
|
||||
self.history_push(history_cell::PlainHistoryCell::new(
|
||||
self.history_push_plain_cell(history_cell::PlainHistoryCell::new(
|
||||
lines,
|
||||
history_cell::HistoryCellType::BackgroundEvent,
|
||||
));
|
||||
|
||||
@@ -287,9 +287,12 @@ pub(super) fn create_diff_summary_with_width(
|
||||
let mut header_spans: Vec<RtSpan<'static>> = Vec::new();
|
||||
// Colorize title: success for apply events, keep primary for approval requests
|
||||
let title_style = match event_type {
|
||||
PatchEventType::ApplyBegin { .. } => Style::default()
|
||||
PatchEventType::ApplyBegin { .. } | PatchEventType::ApplySuccess => Style::default()
|
||||
.fg(crate::colors::success())
|
||||
.add_modifier(Modifier::BOLD),
|
||||
PatchEventType::ApplyFailure => Style::default()
|
||||
.fg(crate::colors::error())
|
||||
.add_modifier(Modifier::BOLD),
|
||||
PatchEventType::ApprovalRequest => Style::default()
|
||||
.fg(crate::colors::primary())
|
||||
.add_modifier(Modifier::BOLD),
|
||||
@@ -346,7 +349,9 @@ pub(super) fn create_diff_summary_with_width(
|
||||
event_type,
|
||||
PatchEventType::ApplyBegin {
|
||||
auto_approved: true
|
||||
} | PatchEventType::ApprovalRequest
|
||||
}
|
||||
| PatchEventType::ApplySuccess
|
||||
| PatchEventType::ApprovalRequest
|
||||
);
|
||||
|
||||
if show_details {
|
||||
|
||||
@@ -623,6 +623,37 @@ impl HistoryState {
|
||||
self.next_id = self.next_id.saturating_add(1);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn apply_event(&mut self, event: HistoryEvent) -> HistoryMutation {
|
||||
match event {
|
||||
HistoryEvent::Insert { index, record } => {
|
||||
let id = self.next_history_id();
|
||||
let record = record.with_id(id);
|
||||
let idx = index.min(self.records.len());
|
||||
self.records.insert(idx, record.clone());
|
||||
HistoryMutation::Inserted { index: idx, id, record }
|
||||
}
|
||||
HistoryEvent::Replace { index, record } => {
|
||||
if let Some(existing) = self.records.get(index) {
|
||||
let id = existing.id();
|
||||
let record = record.with_id(id);
|
||||
self.records[index] = record.clone();
|
||||
HistoryMutation::Replaced { index, id, record }
|
||||
} else {
|
||||
HistoryMutation::Noop
|
||||
}
|
||||
}
|
||||
HistoryEvent::Remove { index } => {
|
||||
if index < self.records.len() {
|
||||
let record = self.records.remove(index);
|
||||
let id = record.id();
|
||||
HistoryMutation::Removed { index, id, record }
|
||||
} else {
|
||||
HistoryMutation::Noop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -708,3 +739,45 @@ impl WithId for HistoryRecord {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HistoryRecord {
|
||||
pub fn id(&self) -> HistoryId {
|
||||
match self {
|
||||
HistoryRecord::PlainMessage(state) => state.id,
|
||||
HistoryRecord::WaitStatus(state) => state.id,
|
||||
HistoryRecord::Loading(state) => state.id,
|
||||
HistoryRecord::RunningTool(state) => state.id,
|
||||
HistoryRecord::ToolCall(state) => state.id,
|
||||
HistoryRecord::PlanUpdate(state) => state.id,
|
||||
HistoryRecord::UpgradeNotice(state) => state.id,
|
||||
HistoryRecord::Reasoning(state) => state.id,
|
||||
HistoryRecord::Exec(state) => state.id,
|
||||
HistoryRecord::AssistantStream(state) => state.id,
|
||||
HistoryRecord::AssistantMessage(state) => state.id,
|
||||
HistoryRecord::Diff(state) => state.id,
|
||||
HistoryRecord::Image(state) => state.id,
|
||||
HistoryRecord::Explore(state) => state.id,
|
||||
HistoryRecord::RateLimits(state) => state.id,
|
||||
HistoryRecord::Patch(state) => state.id,
|
||||
HistoryRecord::BackgroundEvent(state) => state.id,
|
||||
HistoryRecord::Notice(state) => state.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HistoryEvent {
|
||||
Insert { index: usize, record: HistoryRecord },
|
||||
Replace { index: usize, record: HistoryRecord },
|
||||
Remove { index: usize },
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HistoryMutation {
|
||||
Inserted { index: usize, id: HistoryId, record: HistoryRecord },
|
||||
Replaced { index: usize, id: HistoryId, record: HistoryRecord },
|
||||
Removed { index: usize, id: HistoryId, record: HistoryRecord },
|
||||
Noop,
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@ impl AssistantMarkdownCell {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut AssistantMessageState {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
fn rebuild(&mut self, cfg: &codex_core::config::Config) {
|
||||
let mut out: Vec<Line<'static>> = Vec::new();
|
||||
out.push(Line::from("codex"));
|
||||
|
||||
@@ -14,6 +14,14 @@ impl BackgroundEventCell {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &BackgroundEventRecord {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut BackgroundEventRecord {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
fn lines(&self) -> Vec<Line<'static>> {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
let dim_style = Style::default().fg(crate::colors::text_dim());
|
||||
|
||||
@@ -15,6 +15,14 @@ impl DiffCell {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn record(&self) -> &DiffRecord {
|
||||
&self.record
|
||||
}
|
||||
|
||||
pub(crate) fn record_mut(&mut self) -> &mut DiffRecord {
|
||||
&mut self.record
|
||||
}
|
||||
|
||||
pub(crate) fn rebuild_with_theme(&self) {
|
||||
self.layout.borrow_mut().take();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use super::*;
|
||||
use crate::history::state::{HistoryId, ImageRecord};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ImageCellState {
|
||||
@@ -18,13 +20,33 @@ impl ImageCellState {
|
||||
|
||||
pub(crate) struct ImageOutputCell {
|
||||
state: ImageCellState,
|
||||
record: ImageRecord,
|
||||
}
|
||||
|
||||
impl ImageOutputCell {
|
||||
pub(crate) fn new(image: DynamicImage) -> Self {
|
||||
Self {
|
||||
state: ImageCellState::from_dynamic(image),
|
||||
}
|
||||
let state = ImageCellState::from_dynamic(image);
|
||||
let width_u16 = state.width.min(u16::MAX as u32) as u16;
|
||||
let height_u16 = state.height.min(u16::MAX as u32) as u16;
|
||||
let sha = Sha256::digest(&state.rgba);
|
||||
let sha_hex = format!("{:x}", sha);
|
||||
let record = ImageRecord {
|
||||
id: HistoryId::ZERO,
|
||||
source_path: None,
|
||||
alt_text: None,
|
||||
width: width_u16,
|
||||
height: height_u16,
|
||||
sha256: Some(sha_hex),
|
||||
};
|
||||
Self { state, record }
|
||||
}
|
||||
|
||||
pub(crate) fn record(&self) -> &ImageRecord {
|
||||
&self.record
|
||||
}
|
||||
|
||||
pub(crate) fn record_mut(&mut self) -> &mut ImageRecord {
|
||||
&mut self.record
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) struct LoadingCellState {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl LoadingCellState {
|
||||
pub(crate) fn new(message: String) -> Self {
|
||||
Self { message }
|
||||
}
|
||||
}
|
||||
use crate::history::state::{HistoryId, LoadingState};
|
||||
|
||||
pub(crate) struct LoadingCell {
|
||||
state: LoadingCellState,
|
||||
state: LoadingState,
|
||||
}
|
||||
|
||||
impl LoadingCell {
|
||||
pub(crate) fn new(message: String) -> Self {
|
||||
Self {
|
||||
state: LoadingCellState::new(message),
|
||||
state: LoadingState {
|
||||
id: HistoryId::ZERO,
|
||||
message,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_state(state: LoadingState) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &LoadingState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut LoadingState {
|
||||
&mut self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl HistoryCell for LoadingCell {
|
||||
|
||||
@@ -14,10 +14,15 @@ use crate::history::state::{
|
||||
ExecRecord,
|
||||
ExecStatus,
|
||||
HistoryId,
|
||||
RunningToolState,
|
||||
PatchEventType as HistoryPatchEventType,
|
||||
PatchRecord,
|
||||
PlanIcon,
|
||||
PlanProgress,
|
||||
PlanStep,
|
||||
PlanUpdateState,
|
||||
ToolCallState,
|
||||
ToolStatus as HistoryToolStatus,
|
||||
UpgradeNoticeState,
|
||||
};
|
||||
use crate::history::state::{ArgumentValue, ToolArgument, ToolResultPreview};
|
||||
@@ -94,13 +99,7 @@ pub(crate) use explore::{ExploreAggregationCell, ExploreEntryStatus};
|
||||
pub(crate) use image::ImageOutputCell;
|
||||
pub(crate) use loading::LoadingCell;
|
||||
pub(crate) use reasoning::CollapsibleReasoningCell;
|
||||
pub(crate) use tool::{
|
||||
RunningToolCallCell,
|
||||
RunningToolCallState,
|
||||
ToolCallCell,
|
||||
ToolCallCellState,
|
||||
ToolCallStatus,
|
||||
};
|
||||
pub(crate) use tool::{RunningToolCallCell, ToolCallCell};
|
||||
pub(crate) use wait_status::WaitStatusCell;
|
||||
pub(crate) use plan_update::PlanUpdateCell;
|
||||
pub(crate) use plain::PlainHistoryCell;
|
||||
@@ -120,6 +119,8 @@ pub(crate) struct CommandOutput {
|
||||
pub(crate) enum PatchEventType {
|
||||
ApprovalRequest,
|
||||
ApplyBegin { auto_approved: bool },
|
||||
ApplySuccess,
|
||||
ApplyFailure,
|
||||
}
|
||||
|
||||
// ==================== HistoryCellType ====================
|
||||
@@ -132,7 +133,7 @@ pub(crate) enum HistoryCellType {
|
||||
Reasoning,
|
||||
Error,
|
||||
Exec { kind: ExecKind, status: ExecStatus },
|
||||
Tool { status: ToolStatus },
|
||||
Tool { status: ToolCellStatus },
|
||||
Patch { kind: PatchKind },
|
||||
PlanUpdate,
|
||||
BackgroundEvent,
|
||||
@@ -168,12 +169,22 @@ pub(crate) fn action_enum_from_parsed(
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ToolStatus {
|
||||
pub(crate) enum ToolCellStatus {
|
||||
Running,
|
||||
Success,
|
||||
Failed,
|
||||
}
|
||||
|
||||
impl From<HistoryToolStatus> for ToolCellStatus {
|
||||
fn from(status: HistoryToolStatus) -> Self {
|
||||
match status {
|
||||
HistoryToolStatus::Running => ToolCellStatus::Running,
|
||||
HistoryToolStatus::Success => ToolCellStatus::Success,
|
||||
HistoryToolStatus::Failed => ToolCellStatus::Failed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum PatchKind {
|
||||
Proposed,
|
||||
@@ -302,9 +313,9 @@ pub(crate) trait HistoryCell {
|
||||
HistoryCellType::Reasoning => None,
|
||||
HistoryCellType::Error => Some("✖"),
|
||||
HistoryCellType::Tool { status } => Some(match status {
|
||||
ToolStatus::Running => "⚙",
|
||||
ToolStatus::Success => "✔",
|
||||
ToolStatus::Failed => "✖",
|
||||
ToolCellStatus::Running => "⚙",
|
||||
ToolCellStatus::Success => "✔",
|
||||
ToolCellStatus::Failed => "✖",
|
||||
}),
|
||||
HistoryCellType::Exec { kind, status } => {
|
||||
// Show ❯ only for Run executions; hide for read/search/list summaries
|
||||
@@ -4332,17 +4343,18 @@ fn new_exec_command_generic(
|
||||
pub(crate) fn new_active_mcp_tool_call(invocation: McpInvocation) -> ToolCallCell {
|
||||
let invocation_line = format_mcp_invocation(invocation);
|
||||
let invocation_text = line_to_plain_text(&invocation_line);
|
||||
let state = ToolCallCellState::new(
|
||||
"Working".to_string(),
|
||||
ToolCallStatus::Running,
|
||||
None,
|
||||
vec![ToolArgument {
|
||||
let state = ToolCallState {
|
||||
id: HistoryId::ZERO,
|
||||
status: HistoryToolStatus::Running,
|
||||
title: "Working".to_string(),
|
||||
duration: None,
|
||||
arguments: vec![ToolArgument {
|
||||
name: "invocation".to_string(),
|
||||
value: ArgumentValue::Text(invocation_text),
|
||||
}],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
result_preview: None,
|
||||
error_message: None,
|
||||
};
|
||||
ToolCallCell::new(state)
|
||||
}
|
||||
|
||||
@@ -4353,17 +4365,18 @@ pub(crate) fn new_active_custom_tool_call(tool_name: String, args: Option<String
|
||||
} else {
|
||||
format!("{}()", tool_name)
|
||||
};
|
||||
let state = ToolCallCellState::new(
|
||||
"Working".to_string(),
|
||||
ToolCallStatus::Running,
|
||||
None,
|
||||
vec![ToolArgument {
|
||||
let state = ToolCallState {
|
||||
id: HistoryId::ZERO,
|
||||
status: HistoryToolStatus::Running,
|
||||
title: "Working".to_string(),
|
||||
duration: None,
|
||||
arguments: vec![ToolArgument {
|
||||
name: "invocation".to_string(),
|
||||
value: ArgumentValue::Text(invocation_str),
|
||||
}],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
result_preview: None,
|
||||
error_message: None,
|
||||
};
|
||||
ToolCallCell::new(state)
|
||||
}
|
||||
|
||||
@@ -4447,14 +4460,16 @@ pub(crate) fn new_running_browser_tool_call(
|
||||
arguments.append(&mut kv_args);
|
||||
}
|
||||
}
|
||||
RunningToolCallCell::new(RunningToolCallState::new(
|
||||
browser_running_title(&tool_name).to_string(),
|
||||
SystemTime::now(),
|
||||
let state = RunningToolState {
|
||||
id: HistoryId::ZERO,
|
||||
title: browser_running_title(&tool_name).to_string(),
|
||||
started_at: SystemTime::now(),
|
||||
arguments,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
))
|
||||
wait_has_target: false,
|
||||
wait_has_call_id: false,
|
||||
wait_cap_ms: None,
|
||||
};
|
||||
RunningToolCallCell::new(state)
|
||||
}
|
||||
|
||||
fn custom_tool_running_title(tool_name: &str) -> String {
|
||||
@@ -4534,14 +4549,16 @@ pub(crate) fn new_running_custom_tool_call(
|
||||
}
|
||||
}
|
||||
}
|
||||
RunningToolCallCell::new(RunningToolCallState::new(
|
||||
custom_tool_running_title(&tool_name),
|
||||
SystemTime::now(),
|
||||
let state = RunningToolState {
|
||||
id: HistoryId::ZERO,
|
||||
title: custom_tool_running_title(&tool_name),
|
||||
started_at: SystemTime::now(),
|
||||
arguments,
|
||||
wait_has_target,
|
||||
wait_has_call_id,
|
||||
wait_cap_ms,
|
||||
))
|
||||
};
|
||||
RunningToolCallCell::new(state)
|
||||
}
|
||||
|
||||
/// Running web search call (native Responses web_search)
|
||||
@@ -4553,31 +4570,35 @@ pub(crate) fn new_running_web_search(query: Option<String>) -> RunningToolCallCe
|
||||
value: ArgumentValue::Text(q),
|
||||
});
|
||||
}
|
||||
RunningToolCallCell::new(RunningToolCallState::new(
|
||||
"Web Search...".to_string(),
|
||||
SystemTime::now(),
|
||||
let state = RunningToolState {
|
||||
id: HistoryId::ZERO,
|
||||
title: "Web Search...".to_string(),
|
||||
started_at: SystemTime::now(),
|
||||
arguments,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
))
|
||||
wait_has_target: false,
|
||||
wait_has_call_id: false,
|
||||
wait_cap_ms: None,
|
||||
};
|
||||
RunningToolCallCell::new(state)
|
||||
}
|
||||
|
||||
pub(crate) fn new_running_mcp_tool_call(invocation: McpInvocation) -> RunningToolCallCell {
|
||||
// Represent as provider.tool(...) on one dim line beneath a generic running header with timer
|
||||
let line = format_mcp_invocation(invocation);
|
||||
let invocation_text = line_to_plain_text(&line);
|
||||
RunningToolCallCell::new(RunningToolCallState::new(
|
||||
"Working...".to_string(),
|
||||
SystemTime::now(),
|
||||
vec![ToolArgument {
|
||||
let state = RunningToolState {
|
||||
id: HistoryId::ZERO,
|
||||
title: "Working...".to_string(),
|
||||
started_at: SystemTime::now(),
|
||||
arguments: vec![ToolArgument {
|
||||
name: "invocation".to_string(),
|
||||
value: ArgumentValue::Text(invocation_text),
|
||||
}],
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
))
|
||||
wait_has_target: false,
|
||||
wait_has_call_id: false,
|
||||
wait_cap_ms: None,
|
||||
};
|
||||
RunningToolCallCell::new(state)
|
||||
}
|
||||
|
||||
pub(crate) fn new_completed_custom_tool_call(
|
||||
@@ -4596,9 +4617,9 @@ pub(crate) fn new_completed_custom_tool_call(
|
||||
return new_completed_agent_tool_call(tool_name, args, duration, success, result);
|
||||
}
|
||||
let status = if success {
|
||||
ToolCallStatus::Success
|
||||
HistoryToolStatus::Success
|
||||
} else {
|
||||
ToolCallStatus::Failed
|
||||
HistoryToolStatus::Failed
|
||||
};
|
||||
let status_title = if success { "Complete" } else { "Error" };
|
||||
let invocation_str = if let Some(args) = args.clone() {
|
||||
@@ -4643,14 +4664,15 @@ pub(crate) fn new_completed_custom_tool_call(
|
||||
})
|
||||
};
|
||||
|
||||
let state = ToolCallCellState::new(
|
||||
status_title.to_string(),
|
||||
let state = ToolCallState {
|
||||
id: HistoryId::ZERO,
|
||||
status,
|
||||
Some(duration),
|
||||
title: status_title.to_string(),
|
||||
duration: Some(duration),
|
||||
arguments,
|
||||
result_preview,
|
||||
None,
|
||||
);
|
||||
error_message: None,
|
||||
};
|
||||
ToolCallCell::new(state)
|
||||
}
|
||||
|
||||
@@ -4730,9 +4752,9 @@ pub(crate) fn new_completed_web_fetch_tool_call(
|
||||
pre_lines,
|
||||
body_lines,
|
||||
state: if success {
|
||||
ToolCallStatus::Success
|
||||
ToolCellStatus::Success
|
||||
} else {
|
||||
ToolCallStatus::Failed
|
||||
ToolCellStatus::Failed
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -4817,7 +4839,7 @@ fn select_preview_from_plain_text(text: &str, head: usize, tail: usize) -> Vec<L
|
||||
pub(crate) struct WebFetchToolCell {
|
||||
pre_lines: Vec<Line<'static>>, // header/invocation
|
||||
body_lines: Vec<Line<'static>>, // bordered, dim preview
|
||||
state: ToolCallStatus,
|
||||
state: ToolCellStatus,
|
||||
}
|
||||
|
||||
impl HistoryCell for WebFetchToolCell {
|
||||
@@ -4828,13 +4850,7 @@ impl HistoryCell for WebFetchToolCell {
|
||||
self
|
||||
}
|
||||
fn kind(&self) -> HistoryCellType {
|
||||
HistoryCellType::Tool {
|
||||
status: match self.state {
|
||||
ToolCallStatus::Running => ToolStatus::Running,
|
||||
ToolCallStatus::Success => ToolStatus::Success,
|
||||
ToolCallStatus::Failed => ToolStatus::Failed,
|
||||
},
|
||||
}
|
||||
HistoryCellType::Tool { status: self.state }
|
||||
}
|
||||
fn display_lines(&self) -> Vec<Line<'static>> {
|
||||
// Fallback textual representation used only for measurement outside custom render
|
||||
@@ -5213,9 +5229,9 @@ fn new_completed_browser_tool_call(
|
||||
result: String,
|
||||
) -> ToolCallCell {
|
||||
let status = if success {
|
||||
ToolCallStatus::Success
|
||||
HistoryToolStatus::Success
|
||||
} else {
|
||||
ToolCallStatus::Failed
|
||||
HistoryToolStatus::Failed
|
||||
};
|
||||
let mut arguments: Vec<ToolArgument> = Vec::new();
|
||||
if let Some(args_str) = args {
|
||||
@@ -5253,14 +5269,15 @@ fn new_completed_browser_tool_call(
|
||||
})
|
||||
};
|
||||
|
||||
let state = ToolCallCellState::new(
|
||||
browser_tool_title(&tool_name).to_string(),
|
||||
let state = ToolCallState {
|
||||
id: HistoryId::ZERO,
|
||||
status,
|
||||
Some(duration),
|
||||
title: browser_tool_title(&tool_name).to_string(),
|
||||
duration: Some(duration),
|
||||
arguments,
|
||||
result_preview,
|
||||
None,
|
||||
);
|
||||
error_message: None,
|
||||
};
|
||||
ToolCallCell::new(state)
|
||||
}
|
||||
|
||||
@@ -5304,9 +5321,9 @@ fn new_completed_agent_tool_call(
|
||||
result: String,
|
||||
) -> ToolCallCell {
|
||||
let status = if success {
|
||||
ToolCallStatus::Success
|
||||
HistoryToolStatus::Success
|
||||
} else {
|
||||
ToolCallStatus::Failed
|
||||
HistoryToolStatus::Failed
|
||||
};
|
||||
let mut arguments: Vec<ToolArgument> = Vec::new();
|
||||
if let Some(args_str) = args {
|
||||
@@ -5335,14 +5352,15 @@ fn new_completed_agent_tool_call(
|
||||
})
|
||||
};
|
||||
|
||||
let state = ToolCallCellState::new(
|
||||
agent_tool_title(&tool_name),
|
||||
let state = ToolCallState {
|
||||
id: HistoryId::ZERO,
|
||||
status,
|
||||
Some(duration),
|
||||
title: agent_tool_title(&tool_name),
|
||||
duration: Some(duration),
|
||||
arguments,
|
||||
result_preview,
|
||||
None,
|
||||
);
|
||||
error_message: None,
|
||||
};
|
||||
ToolCallCell::new(state)
|
||||
}
|
||||
|
||||
@@ -5397,9 +5415,9 @@ pub(crate) fn new_completed_mcp_tool_call(
|
||||
}
|
||||
|
||||
let status = if success {
|
||||
ToolCallStatus::Success
|
||||
HistoryToolStatus::Success
|
||||
} else {
|
||||
ToolCallStatus::Failed
|
||||
HistoryToolStatus::Failed
|
||||
};
|
||||
|
||||
let invocation_line = format_mcp_invocation(invocation);
|
||||
@@ -5459,14 +5477,15 @@ pub(crate) fn new_completed_mcp_tool_call(
|
||||
})
|
||||
};
|
||||
|
||||
let state = ToolCallCellState::new(
|
||||
if success { "Complete" } else { "Error" }.to_string(),
|
||||
let state = ToolCallState {
|
||||
id: HistoryId::ZERO,
|
||||
status,
|
||||
Some(duration),
|
||||
title: if success { "Complete" } else { "Error" }.to_string(),
|
||||
duration: Some(duration),
|
||||
arguments,
|
||||
result_preview,
|
||||
error_message,
|
||||
);
|
||||
};
|
||||
|
||||
Box::new(ToolCallCell::new(state))
|
||||
}
|
||||
@@ -5826,16 +5845,31 @@ pub(crate) fn new_patch_event(
|
||||
let title = match event_type {
|
||||
PatchEventType::ApprovalRequest => "proposed patch".to_string(),
|
||||
PatchEventType::ApplyBegin { .. } => "Updated".to_string(),
|
||||
PatchEventType::ApplySuccess => "Patch applied".to_string(),
|
||||
PatchEventType::ApplyFailure => "Patch failed".to_string(),
|
||||
};
|
||||
let kind = match event_type {
|
||||
PatchEventType::ApprovalRequest => PatchKind::Proposed,
|
||||
PatchEventType::ApplyBegin { .. } => PatchKind::ApplyBegin,
|
||||
PatchEventType::ApplySuccess => PatchKind::ApplySuccess,
|
||||
PatchEventType::ApplyFailure => PatchKind::ApplyFailure,
|
||||
};
|
||||
let record = PatchRecord {
|
||||
id: HistoryId::ZERO,
|
||||
patch_type: match event_type {
|
||||
PatchEventType::ApprovalRequest => HistoryPatchEventType::ApprovalRequest,
|
||||
PatchEventType::ApplyBegin { auto_approved } => {
|
||||
HistoryPatchEventType::ApplyBegin { auto_approved }
|
||||
}
|
||||
PatchEventType::ApplySuccess => HistoryPatchEventType::ApplySuccess,
|
||||
PatchEventType::ApplyFailure => HistoryPatchEventType::ApplyFailure,
|
||||
},
|
||||
changes,
|
||||
};
|
||||
PatchSummaryCell {
|
||||
title,
|
||||
changes,
|
||||
event_type,
|
||||
kind,
|
||||
record,
|
||||
cached: std::cell::RefCell::new(None),
|
||||
}
|
||||
}
|
||||
@@ -5879,9 +5913,8 @@ pub(crate) fn new_patch_apply_failure(stderr: String) -> PlainHistoryCell {
|
||||
|
||||
pub(crate) struct PatchSummaryCell {
|
||||
pub(crate) title: String,
|
||||
pub(crate) changes: HashMap<PathBuf, FileChange>,
|
||||
pub(crate) event_type: PatchEventType,
|
||||
pub(crate) kind: PatchKind,
|
||||
pub(crate) record: PatchRecord,
|
||||
// Cache width-specific rendered lines to avoid repeated filesystem reads
|
||||
// and pre-wrapping work inside create_diff_summary_with_width.
|
||||
cached: std::cell::RefCell<Option<PatchLayoutCache>>,
|
||||
@@ -5894,6 +5927,25 @@ struct PatchLayoutCache {
|
||||
}
|
||||
|
||||
impl PatchSummaryCell {
|
||||
fn ui_event_type(&self) -> PatchEventType {
|
||||
match self.record.patch_type {
|
||||
HistoryPatchEventType::ApprovalRequest => PatchEventType::ApprovalRequest,
|
||||
HistoryPatchEventType::ApplyBegin { auto_approved } => {
|
||||
PatchEventType::ApplyBegin { auto_approved }
|
||||
}
|
||||
HistoryPatchEventType::ApplySuccess => PatchEventType::ApplySuccess,
|
||||
HistoryPatchEventType::ApplyFailure => PatchEventType::ApplyFailure,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn record(&self) -> &PatchRecord {
|
||||
&self.record
|
||||
}
|
||||
|
||||
pub(crate) fn record_mut(&mut self) -> &mut PatchRecord {
|
||||
&mut self.record
|
||||
}
|
||||
|
||||
fn ensure_lines(&self, width: u16) -> Vec<Line<'static>> {
|
||||
if let Some(c) = self.cached.borrow().as_ref() {
|
||||
if c.width == width {
|
||||
@@ -5902,8 +5954,8 @@ impl PatchSummaryCell {
|
||||
}
|
||||
let lines: Vec<Line<'static>> = create_diff_summary_with_width(
|
||||
&self.title,
|
||||
&self.changes,
|
||||
self.event_type,
|
||||
&self.record.changes,
|
||||
self.ui_event_type(),
|
||||
Some(width as usize),
|
||||
)
|
||||
.into_iter()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Rendering for structured plan update history cells built from `PlanUpdateState`.
|
||||
|
||||
use super::*;
|
||||
use crate::history::state::{PlanIcon, PlanProgress, PlanStep, PlanUpdateState};
|
||||
use crate::history::state::{HistoryId, PlanIcon, PlanProgress, PlanStep, PlanUpdateState};
|
||||
use codex_core::plan_tool::StepStatus;
|
||||
|
||||
pub(crate) struct PlanUpdateCell {
|
||||
@@ -10,6 +10,8 @@ pub(crate) struct PlanUpdateCell {
|
||||
|
||||
impl PlanUpdateCell {
|
||||
pub(crate) fn new(state: PlanUpdateState) -> Self {
|
||||
let mut state = state;
|
||||
state.id = HistoryId::ZERO;
|
||||
Self { state }
|
||||
}
|
||||
|
||||
@@ -18,6 +20,19 @@ impl PlanUpdateCell {
|
||||
progress.total > 0 && progress.completed >= progress.total
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_state(state: PlanUpdateState) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &PlanUpdateState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut PlanUpdateState {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
fn header_line(&self) -> Line<'static> {
|
||||
let progress = &self.state.progress;
|
||||
let is_complete = self.is_complete();
|
||||
|
||||
@@ -4,7 +4,14 @@ use super::semantic::SemanticLine;
|
||||
use super::text;
|
||||
use super::*;
|
||||
use crate::history::state::{
|
||||
BulletMarker, InlineSpan, ReasoningBlock, ReasoningSection, TextEmphasis, TextTone,
|
||||
BulletMarker,
|
||||
HistoryId,
|
||||
InlineSpan,
|
||||
ReasoningBlock,
|
||||
ReasoningSection,
|
||||
ReasoningState,
|
||||
TextEmphasis,
|
||||
TextTone,
|
||||
};
|
||||
use crate::history_cell::assistant::detect_bullet_prefix;
|
||||
use crate::render::line_utils;
|
||||
@@ -19,6 +26,7 @@ pub(crate) struct CollapsibleReasoningState {
|
||||
pub in_progress: bool,
|
||||
pub hide_when_collapsed: bool,
|
||||
pub id: Option<String>,
|
||||
pub history_id: HistoryId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -43,6 +51,7 @@ impl CollapsibleReasoningState {
|
||||
in_progress: false,
|
||||
hide_when_collapsed: false,
|
||||
id,
|
||||
history_id: HistoryId::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,6 +123,20 @@ impl CollapsibleReasoningCell {
|
||||
let theme = crate::theme::current_theme();
|
||||
debug_title_overlay(§ions_to_ratatui_lines(&state.sections, &theme))
|
||||
}
|
||||
|
||||
pub(crate) fn set_history_id(&self, id: HistoryId) {
|
||||
self.state.borrow_mut().history_id = id;
|
||||
}
|
||||
|
||||
pub(crate) fn reasoning_state(&self) -> ReasoningState {
|
||||
let state = self.state.borrow();
|
||||
ReasoningState {
|
||||
id: state.history_id,
|
||||
sections: state.sections.clone(),
|
||||
effort: None,
|
||||
in_progress: state.in_progress,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HistoryCell for CollapsibleReasoningCell {
|
||||
|
||||
@@ -301,4 +301,12 @@ impl StreamingContentCell {
|
||||
fn ensure_stream_layout(&self, width: u16) -> AssistantLayoutCache {
|
||||
self.assistant.ensure_layout(width)
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &AssistantStreamState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut AssistantStreamState {
|
||||
&mut self.state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,50 @@
|
||||
//! Tool call history cells driven by structured argument/result state.
|
||||
|
||||
use super::*;
|
||||
use crate::history::state::{ArgumentValue, ToolArgument, ToolResultPreview};
|
||||
use crate::history::state::{
|
||||
ArgumentValue,
|
||||
HistoryId,
|
||||
RunningToolState,
|
||||
ToolArgument,
|
||||
ToolCallState,
|
||||
ToolStatus as HistoryToolStatus,
|
||||
};
|
||||
use crate::text_formatting::format_json_compact;
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ToolCallStatus {
|
||||
Running,
|
||||
Success,
|
||||
Failed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ToolCallCellState {
|
||||
pub title: String,
|
||||
pub status: ToolCallStatus,
|
||||
pub duration: Option<Duration>,
|
||||
pub arguments: Vec<ToolArgument>,
|
||||
pub result_preview: Option<ToolResultPreview>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
impl ToolCallCellState {
|
||||
pub(crate) fn new(
|
||||
title: String,
|
||||
status: ToolCallStatus,
|
||||
duration: Option<Duration>,
|
||||
arguments: Vec<ToolArgument>,
|
||||
result_preview: Option<ToolResultPreview>,
|
||||
error_message: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
title,
|
||||
status,
|
||||
duration,
|
||||
arguments,
|
||||
result_preview,
|
||||
error_message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ToolCallCell {
|
||||
state: ToolCallCellState,
|
||||
state: ToolCallState,
|
||||
}
|
||||
|
||||
impl ToolCallCell {
|
||||
pub(crate) fn new(state: ToolCallCellState) -> Self {
|
||||
pub(crate) fn new(state: ToolCallState) -> Self {
|
||||
let mut state = state;
|
||||
state.id = HistoryId::ZERO;
|
||||
Self { state }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_state(state: ToolCallState) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &ToolCallState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut ToolCallState {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
pub(crate) fn retint(&mut self, _old: &crate::theme::Theme, _new: &crate::theme::Theme) {}
|
||||
|
||||
fn header_line(&self) -> Line<'static> {
|
||||
let mut spans: Vec<Span<'static>> = Vec::new();
|
||||
let mut style = Style::default().add_modifier(Modifier::BOLD);
|
||||
style = match self.state.status {
|
||||
ToolCallStatus::Running => style.fg(crate::colors::info()),
|
||||
ToolCallStatus::Success => style.fg(crate::colors::success()),
|
||||
ToolCallStatus::Failed => style.fg(crate::colors::error()),
|
||||
HistoryToolStatus::Running => style.fg(crate::colors::info()),
|
||||
HistoryToolStatus::Success => style.fg(crate::colors::success()),
|
||||
HistoryToolStatus::Failed => style.fg(crate::colors::error()),
|
||||
};
|
||||
spans.push(Span::styled(self.state.title.clone(), style));
|
||||
if let Some(duration) = self.state.duration {
|
||||
@@ -83,11 +68,7 @@ impl HistoryCell for ToolCallCell {
|
||||
|
||||
fn kind(&self) -> HistoryCellType {
|
||||
HistoryCellType::Tool {
|
||||
status: match self.state.status {
|
||||
ToolCallStatus::Running => ToolStatus::Running,
|
||||
ToolCallStatus::Success => ToolStatus::Success,
|
||||
ToolCallStatus::Failed => ToolStatus::Failed,
|
||||
},
|
||||
status: super::ToolCellStatus::from(self.state.status),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,49 +110,37 @@ impl HistoryCell for ToolCallCell {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct RunningToolCallState {
|
||||
pub title: String,
|
||||
pub started_at: SystemTime,
|
||||
pub arguments: Vec<ToolArgument>,
|
||||
pub wait_has_target: bool,
|
||||
pub wait_has_call_id: bool,
|
||||
pub wait_cap_ms: Option<u64>,
|
||||
}
|
||||
|
||||
impl RunningToolCallState {
|
||||
pub(crate) fn new(
|
||||
title: String,
|
||||
started_at: SystemTime,
|
||||
arguments: Vec<ToolArgument>,
|
||||
wait_has_target: bool,
|
||||
wait_has_call_id: bool,
|
||||
wait_cap_ms: Option<u64>,
|
||||
) -> Self {
|
||||
Self {
|
||||
title,
|
||||
started_at,
|
||||
arguments,
|
||||
wait_has_target,
|
||||
wait_has_call_id,
|
||||
wait_cap_ms,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RunningToolCallCell {
|
||||
state: RunningToolCallState,
|
||||
state: RunningToolState,
|
||||
start_clock: Instant,
|
||||
}
|
||||
|
||||
impl RunningToolCallCell {
|
||||
pub(crate) fn new(state: RunningToolCallState) -> Self {
|
||||
pub(crate) fn new(state: RunningToolState) -> Self {
|
||||
let mut state = state;
|
||||
state.id = HistoryId::ZERO;
|
||||
Self {
|
||||
state,
|
||||
start_clock: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_state(state: RunningToolState) -> Self {
|
||||
Self {
|
||||
state,
|
||||
start_clock: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &RunningToolState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut RunningToolState {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
fn strip_zero_seconds_suffix(mut duration: String) -> String {
|
||||
if duration.ends_with(" 00s") {
|
||||
duration.truncate(duration.len().saturating_sub(4));
|
||||
@@ -203,22 +172,23 @@ impl RunningToolCallCell {
|
||||
});
|
||||
}
|
||||
let status = if success {
|
||||
ToolCallStatus::Success
|
||||
HistoryToolStatus::Success
|
||||
} else {
|
||||
ToolCallStatus::Failed
|
||||
HistoryToolStatus::Failed
|
||||
};
|
||||
let state = ToolCallCellState::new(
|
||||
if success {
|
||||
let state = ToolCallState {
|
||||
id: HistoryId::ZERO,
|
||||
status,
|
||||
title: if success {
|
||||
"Web Search".to_string()
|
||||
} else {
|
||||
"Web Search (failed)".to_string()
|
||||
},
|
||||
status,
|
||||
Some(duration),
|
||||
duration: Some(duration),
|
||||
arguments,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
result_preview: None,
|
||||
error_message: None,
|
||||
};
|
||||
ToolCallCell::new(state)
|
||||
}
|
||||
|
||||
@@ -240,7 +210,7 @@ impl HistoryCell for RunningToolCallCell {
|
||||
|
||||
fn kind(&self) -> HistoryCellType {
|
||||
HistoryCellType::Tool {
|
||||
status: ToolStatus::Running,
|
||||
status: super::ToolCellStatus::Running,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Upgrade notice cell built from `UpgradeNoticeState` metadata.
|
||||
|
||||
use super::*;
|
||||
use crate::history::state::UpgradeNoticeState;
|
||||
use crate::history::state::{HistoryId, UpgradeNoticeState};
|
||||
|
||||
const TARGET_WIDTH: u16 = 70;
|
||||
|
||||
@@ -13,6 +13,17 @@ pub(crate) struct UpgradeNoticeCell {
|
||||
|
||||
impl UpgradeNoticeCell {
|
||||
pub(crate) fn new(state: UpgradeNoticeState) -> Self {
|
||||
let mut state = state;
|
||||
state.id = HistoryId::ZERO;
|
||||
Self::with_state(state)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_state(state: UpgradeNoticeState) -> Self {
|
||||
Self::with_state(state)
|
||||
}
|
||||
|
||||
fn with_state(state: UpgradeNoticeState) -> Self {
|
||||
let primary = crate::colors::primary();
|
||||
let backdrop = crate::colors::mix_toward(primary, crate::colors::background(), 0.95);
|
||||
let border_style = Style::default().bg(backdrop).fg(primary);
|
||||
@@ -23,6 +34,14 @@ impl UpgradeNoticeCell {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &UpgradeNoticeState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut UpgradeNoticeState {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
fn text(&self) -> Text<'static> {
|
||||
Text::from(self.message_lines())
|
||||
}
|
||||
|
||||
@@ -15,6 +15,19 @@ impl WaitStatusCell {
|
||||
}
|
||||
|
||||
pub(crate) fn retint(&mut self, _old: &crate::theme::Theme, _new: &crate::theme::Theme) {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_state(state: WaitStatusState) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &WaitStatusState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn state_mut(&mut self) -> &mut WaitStatusState {
|
||||
&mut self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl HistoryCell for WaitStatusCell {
|
||||
|
||||
Reference in New Issue
Block a user