From d03328d535881e4efe16c168753c9c5c1b400e19 Mon Sep 17 00:00:00 2001 From: Ariestar <1941137088@qq.com> Date: Thu, 13 Aug 2026 15:25:23 +0800 Subject: [PATCH 01/10] feat(tui): click a structure tag to expand its block in read mode Read mode folds every structure block to its <:...:> tag line; clicking a tag expands that block to its full payload (grok-build style), and clicking the tag again collapses it. Raw mode always shows full blocks and ignores the expand state. Tags keep the same gray style in both modes. Per-half expansion state lives in the picker and resets when the shown dialogue, target, or selection changes; line counts, scrolling, and visual selection stay consistent because expansion is applied while the display text is built. --- src/commands/browse/help.rs | 11 +- src/commands/browse/panes.rs | 8 +- src/commands/browse/picker.rs | 41 +++++++- src/tui/content/io.rs | 30 ++++++ src/tui/content/text.rs | 188 +++++++++++++++++++++++++++++----- src/tui/workspace/mod.rs | 5 +- src/tui/workspace/model.rs | 21 +++- 7 files changed, 260 insertions(+), 44 deletions(-) diff --git a/src/commands/browse/help.rs b/src/commands/browse/help.rs index d2d9a107..deaf40bb 100644 --- a/src/commands/browse/help.rs +++ b/src/commands/browse/help.rs @@ -8,9 +8,10 @@ use ratatui::widgets::ListState; use crate::tui::content::view::ContentViewMode; use crate::tui::terminal::suspend; use crate::tui::workspace::{ - can_open_dialogue_vim, selected_index, workspace_content_io_texts, workspace_content_text, - workspace_layout, ContentIoFocus, ContentIoFrame, ContentScrolls, WorkspaceDialogue, - WorkspaceFocus, WorkspaceHelpAction, WorkspacePickedContent, WorkspaceSession, WorkspaceSource, + can_open_dialogue_vim, selected_index, workspace_content_io_texts_expanded, + workspace_content_text, workspace_layout, ContentIoFocus, ContentIoFrame, ContentScrolls, + ExpandedBlocks, WorkspaceDialogue, WorkspaceFocus, WorkspaceHelpAction, WorkspacePickedContent, + WorkspaceSession, WorkspaceSource, }; use sivtr_core::record::WorkAt; @@ -51,6 +52,7 @@ pub(super) fn apply_workspace_help_action( content_scrolls: &mut ContentScrolls, content_io_focus: &mut ContentIoFocus, content_mode: &mut ContentViewMode, + expanded: &ExpandedBlocks, content_input_lines: usize, content_output_lines: usize, show_help: &mut bool, @@ -246,12 +248,13 @@ pub(super) fn apply_workspace_help_action( *focus, *fullscreen, ); - let io = workspace_content_io_texts( + let io = workspace_content_io_texts_expanded( dialogues, selected_dialogues, dialogue_idx, *content_mode, content_at, + expanded, ); let frame = ContentIoFrame::build(layout.content, io, *content_mode, *content_io_focus); let active = frame.active(*content_io_focus, content_scrolls); diff --git a/src/commands/browse/panes.rs b/src/commands/browse/panes.rs index 0c3dbf8f..e99fe06b 100644 --- a/src/commands/browse/panes.rs +++ b/src/commands/browse/panes.rs @@ -10,8 +10,8 @@ use crate::pane::{Pane, PaneInput, SlidingPane, WindowRow}; use crate::tui::content::view::ContentViewMode; use crate::tui::workspace::{ - workspace_content_io_texts, ContentIoFocus, ContentIoFrame, ContentIoTexts, WorkspaceDialogue, - WorkspaceSession, WorkspaceSource, + workspace_content_io_texts_expanded, ContentIoFocus, ContentIoFrame, ContentIoTexts, + ExpandedBlocks, WorkspaceDialogue, WorkspaceSession, WorkspaceSource, }; use sivtr_core::ai::AgentSelection; use sivtr_core::record::{WorkAt, WorkRecord, WorkRef}; @@ -419,6 +419,7 @@ pub struct ContentCtx<'a> { pub target: Option, pub area: ratatui::layout::Rect, pub io_focus: ContentIoFocus, + pub expanded: ExpandedBlocks, } /// Tracks layout line counts for Input / Output halves separately. @@ -438,12 +439,13 @@ impl ContentPane { /// Build texts + dynamic layout metrics for this frame. pub fn ensure(&mut self, ctx: ContentCtx<'_>) -> ContentIoTexts { - let texts = workspace_content_io_texts( + let texts = workspace_content_io_texts_expanded( ctx.dialogues, ctx.selected_dialogues, ctx.highlighted_idx, ctx.mode, ctx.target, + &ctx.expanded, ); let frame = ContentIoFrame::build(ctx.area, texts, ctx.mode, ctx.io_focus); self.input_lines = frame.input_lines; diff --git a/src/commands/browse/picker.rs b/src/commands/browse/picker.rs index 6d364f0f..c8aed839 100644 --- a/src/commands/browse/picker.rs +++ b/src/commands/browse/picker.rs @@ -6,7 +6,7 @@ use ratatui::widgets::ListState; use std::collections::HashSet; use std::path::PathBuf; -use crate::tui::content::view::{content_link_at, ContentViewMode}; +use crate::tui::content::view::{content_link_at, content_position_at, ContentViewMode}; use crate::tui::search::{ workspace_search_fingerprint, workspace_search_has_query, workspace_search_scope, WorkspaceSearchIndex, WorkspaceSearchOutput, @@ -14,9 +14,10 @@ use crate::tui::search::{ use crate::tui::terminal::read_interaction; use crate::tui::workspace::{ help_action_for_key, panel_inner_rows, render_workspace, search_match_half, selected_index, - workspace_help_entries, workspace_hit_test, workspace_layout, ContentIoFocus, ContentIoFrame, - ContentScrolls, WorkspaceDialogue, WorkspaceFocus, WorkspacePickedContent, WorkspaceSearchView, - WorkspaceSession, WorkspaceSource, WorkspaceView, + structure_block_index, workspace_help_entries, workspace_hit_test, workspace_layout, + ContentIoFocus, ContentIoFrame, ContentScrolls, ExpandedBlocks, WorkspaceDialogue, + WorkspaceFocus, WorkspacePickedContent, WorkspaceSearchView, WorkspaceSession, WorkspaceSource, + WorkspaceView, }; use super::content::{ @@ -80,6 +81,8 @@ pub(crate) fn run( let mut content_scrolls = ContentScrolls::default(); let mut content_io_focus = ContentIoFocus::Input; let mut content_mode = ContentViewMode::Reading; + let mut expanded_blocks = ExpandedBlocks::default(); + let mut expanded_key = None; let mut show_help = false; let mut show_search = false; let mut search_query = String::new(); @@ -324,6 +327,13 @@ pub(crate) fn run( if let Some((half, _)) = pending_half { content_io_focus = half; } + // Expansion indices are per-dialogue; reset when the shown + // dialogue, target, or selection changes. + let expand_key = (dialogue_idx, active_content_at, selected_dialogues.clone()); + if expanded_key.as_ref() != Some(&expand_key) { + expanded_blocks.clear(); + expanded_key = Some(expand_key); + } content_frame = ContentIoFrame::build( layout.content, content_pane.ensure(ContentCtx { @@ -334,6 +344,7 @@ pub(crate) fn run( target: active_content_at, area: layout.content, io_focus: content_io_focus, + expanded: expanded_blocks.clone(), }), content_mode, content_io_focus, @@ -650,6 +661,7 @@ pub(crate) fn run( &mut content_scrolls, &mut content_io_focus, &mut content_mode, + &expanded_blocks, content_pane.line_count(ContentIoFocus::Input), content_pane.line_count(ContentIoFocus::Output), &mut show_help, @@ -763,6 +775,7 @@ pub(crate) fn run( &mut content_scrolls, &mut content_io_focus, &mut content_mode, + &expanded_blocks, content_pane.line_count(ContentIoFocus::Input), content_pane.line_count(ContentIoFocus::Output), &mut show_help, @@ -857,6 +870,26 @@ pub(crate) fn run( let _ = open_link_target(&target); continue; } + // Read mode: clicking a structure tag expands/collapses + // that block (raw mode always shows full blocks). + if content_mode == ContentViewMode::Reading { + if let Some(position) = content_position_at( + active.area, + active.text, + *active.scroll, + content_mode, + mouse.column, + mouse.row, + ) { + if let Some(block) = + structure_block_index(active.text, position.line) + { + expanded_blocks.toggle(half, block); + redraw = true; + continue; + } + } + } } if handle_content_mouse_select( &mut visual_select_mode, diff --git a/src/tui/content/io.rs b/src/tui/content/io.rs index 6f9f27bd..f7c1e49d 100644 --- a/src/tui/content/io.rs +++ b/src/tui/content/io.rs @@ -4,6 +4,7 @@ //! so picker / render / help don't re-copy the same match arms. use ratatui::layout::Rect; +use std::collections::HashSet; use crate::tui::content::view::{content_view_line_count, ContentViewMode}; @@ -117,6 +118,35 @@ impl ContentScrolls { } } +/// Which structure blocks the user expanded, per content half. Read mode +/// defaults every block to its `<:…:>` tag line; a block in this set shows +/// its full payload instead. Raw mode always shows full blocks and ignores +/// this state. A block index is the ordinal of the block's open marker line +/// within that half's text — stable because every block shows exactly one +/// open marker line whether collapsed or expanded. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub(crate) struct ExpandedBlocks { + pub(crate) input: HashSet, + pub(crate) output: HashSet, +} + +impl ExpandedBlocks { + pub(crate) fn toggle(&mut self, focus: ContentIoFocus, block: usize) { + let set = match focus { + ContentIoFocus::Input => &mut self.input, + ContentIoFocus::Output => &mut self.output, + }; + if !set.insert(block) { + set.remove(&block); + } + } + + pub(crate) fn clear(&mut self) { + self.input.clear(); + self.output.clear(); + } +} + /// Geometry of the dual content panes. #[derive(Clone, Copy, Debug, Default)] pub(crate) struct ContentIoAreas { diff --git a/src/tui/content/text.rs b/src/tui/content/text.rs index aeac50e7..2fb8932b 100644 --- a/src/tui/content/text.rs +++ b/src/tui/content/text.rs @@ -2,18 +2,30 @@ use sivtr_core::record::{WorkAt, WorkRecord}; -use crate::tui::content::io::ContentIoTexts; +use crate::tui::content::io::{ContentIoTexts, ExpandedBlocks}; use crate::tui::content::view::ContentViewMode; use crate::tui::workspace::model::WorkspaceDialogue; -pub(crate) fn content_io_from_record(record: &WorkRecord, reading: bool) -> ContentIoTexts { +/// Read mode folds every structure part to its `<:…:>` tag; parts listed in +/// `expanded` show their full block instead. Raw mode always shows full +/// blocks (the expand state only affects reading). +pub(crate) fn content_io_from_record_expanded( + record: &WorkRecord, + reading: bool, + expanded: &ExpandedBlocks, +) -> ContentIoTexts { ContentIoTexts { - input: io_body_text(record, reading, true), - output: io_body_text(record, reading, false), + input: io_body_text(record, reading, true, &expanded.input), + output: io_body_text(record, reading, false, &expanded.output), } } -fn io_body_text(record: &WorkRecord, reading: bool, input: bool) -> String { +fn io_body_text( + record: &WorkRecord, + reading: bool, + input: bool, + expanded: &std::collections::HashSet, +) -> String { let parts: Vec<&sivtr_core::record::WorkPart> = record .parts .iter() @@ -22,20 +34,24 @@ fn io_body_text(record: &WorkRecord, reading: bool, input: bool) -> String { if parts.is_empty() { return String::new(); } - if reading { - structured_parts_text(&parts) - } else { - raw_parts_text(&parts) - } -} - -/// Reading: dialogue in order; each structure part collapses to its -/// `<:…:>` open-marker tag line (same gray style as raw markers). -fn structured_parts_text(parts: &[&sivtr_core::record::WorkPart]) -> String { + let mut block = 0usize; let mut chunks = Vec::new(); - for part in parts { + for part in &parts { if part.kind().is_structure() { - chunks.push(structure_fold_label(part)); + let idx = block; + block += 1; + // Raw mode always shows full blocks; read mode folds to the tag + // unless the block was expanded. + let show_full = if reading { + expanded.contains(&idx) + } else { + true + }; + if show_full { + chunks.push(sivtr_core::record::format_work_part(part)); + } else { + chunks.push(structure_fold_label(part)); + } } else { chunks.push(part.text().into_owned()); } @@ -43,14 +59,6 @@ fn structured_parts_text(parts: &[&sivtr_core::record::WorkPart]) -> String { chunks.join("\n\n") } -fn raw_parts_text(parts: &[&sivtr_core::record::WorkPart]) -> String { - parts - .iter() - .map(|part| sivtr_core::record::format_work_part(part)) - .collect::>() - .join("\n\n") -} - pub(crate) fn structured_part_text(part: &sivtr_core::record::WorkPart) -> String { if part.kind().is_structure() { structure_fold_label(part) @@ -77,13 +85,32 @@ pub(crate) fn workspace_content_text( .join_displayed() } -/// Input / Output bodies for the dual content panes. +/// Input / Output bodies for the dual content panes (default expand state). pub(crate) fn workspace_content_io_texts( dialogues: &[WorkspaceDialogue], selected_dialogues: &[bool], highlighted_idx: usize, mode: ContentViewMode, target: Option, +) -> ContentIoTexts { + workspace_content_io_texts_expanded( + dialogues, + selected_dialogues, + highlighted_idx, + mode, + target, + &ExpandedBlocks::default(), + ) +} + +/// Input / Output bodies for the dual content panes with per-block expansion. +pub(crate) fn workspace_content_io_texts_expanded( + dialogues: &[WorkspaceDialogue], + selected_dialogues: &[bool], + highlighted_idx: usize, + mode: ContentViewMode, + target: Option, + expanded: &ExpandedBlocks, ) -> ContentIoTexts { if dialogues.is_empty() { return ContentIoTexts { @@ -101,7 +128,7 @@ pub(crate) fn workspace_content_io_texts( if selected.is_empty() { return dialogues .get(highlighted_idx) - .map(|dialogue| dialogue.content_io_texts(mode, target)) + .map(|dialogue| dialogue.content_io_texts_expanded(mode, target, expanded)) .unwrap_or_else(|| ContentIoTexts { input: "".to_string(), output: String::new(), @@ -115,7 +142,7 @@ pub(crate) fn workspace_content_io_texts( let Some(dialogue) = dialogues.get(dialogue_idx) else { continue; }; - let io = dialogue.content_io_texts(mode, None); + let io = dialogue.content_io_texts_expanded(mode, None, expanded); if !io.input.trim().is_empty() { input.push(io.input); } @@ -136,3 +163,108 @@ pub(crate) fn workspace_content_io_texts( }, } } + +/// Index of the structure block whose open marker sits on `line` of the +/// displayed text, if any. Every block contributes exactly one open marker +/// line — collapsed (tag only) or expanded — so the marker ordinal is the +/// block index. +pub(crate) fn structure_block_index(text: &str, line: usize) -> Option { + let mut block = 0usize; + for (idx, text_line) in text.lines().enumerate() { + if is_structure_open_marker(text_line) { + if idx == line { + return Some(block); + } + block += 1; + } + } + None +} + +fn is_structure_open_marker(line: &str) -> bool { + line.starts_with("<:") && !line.starts_with("<:/") +} + +#[cfg(test)] +mod tests { + use super::{content_io_from_record_expanded, structure_block_index}; + use crate::tui::content::io::{ContentIoFocus, ExpandedBlocks}; + use sivtr_core::ai::AgentProvider; + use sivtr_core::record::{WorkPart, WorkPartData, WorkRecord, WorkRef}; + + fn tool_part(seq: usize, tool: &str, input: &str) -> WorkPart { + WorkPart { + seq, + occurred_at: None, + data: WorkPartData::ToolCall { + call_id: None, + tool: Some(tool.to_string()), + input: serde_json::json!({ "command": input }), + }, + } + } + + fn record(parts: Vec) -> WorkRecord { + WorkRecord { + schema_version: 2, + work_ref: WorkRef::agent(AgentProvider::Codex, "session", 1), + kind: sivtr_core::record::WorkRecordKind::ChatTurn, + source: sivtr_core::record::WorkSource { + channel: sivtr_core::record::WorkChannel::Chat, + provider: Some("codex".to_string()), + }, + session: sivtr_core::record::WorkSessionRef { + id: "session".to_string(), + canonical_id: None, + path: None, + }, + cwd: None, + time: sivtr_core::record::WorkTime::default(), + status: None, + title: "cmd".to_string(), + parts, + } + } + + #[test] + fn reading_folds_to_tags_and_expanded_blocks_show_full() { + let rec = record(vec![ + tool_part(1, "Bash", "ls"), + tool_part(2, "Read", "file"), + ]); + let io = content_io_from_record_expanded(&rec, true, &ExpandedBlocks::default()); + let output = &io.output; + assert!(output.contains("<:tool:Bash call:>")); + assert!(output.contains("<:tool:Read call:>")); + assert!(!output.contains("ls")); + assert!(!output.contains("file")); + + let mut expanded = ExpandedBlocks::default(); + expanded.toggle(ContentIoFocus::Output, 1); + let io = content_io_from_record_expanded(&rec, true, &expanded); + let output = &io.output; + assert!(output.contains("<:tool:Read call:>")); + assert!(output.contains("<:/tool:Read call:>")); + assert!(output.contains("file")); + assert!(!output.contains("ls")); + } + + #[test] + fn raw_mode_ignores_expand_state() { + let rec = record(vec![tool_part(1, "Bash", "ls")]); + let mut expanded = ExpandedBlocks::default(); + expanded.toggle(ContentIoFocus::Output, 0); + let io = content_io_from_record_expanded(&rec, false, &expanded); + assert!(io.output.contains("<:tool:Bash call:>")); + assert!(io.output.contains("ls")); + assert!(io.output.contains("<:/tool:Bash call:>")); + } + + #[test] + fn block_index_maps_displayed_markers() { + let text = "<:tool:A call:>\nbody\n\n<:tool:B call:>"; + assert_eq!(structure_block_index(text, 0), Some(0)); + assert_eq!(structure_block_index(text, 3), Some(1)); + assert_eq!(structure_block_index(text, 1), None); + } +} diff --git a/src/tui/workspace/mod.rs b/src/tui/workspace/mod.rs index b6a1fd03..051425c4 100644 --- a/src/tui/workspace/mod.rs +++ b/src/tui/workspace/mod.rs @@ -13,8 +13,11 @@ pub(crate) use render::*; // Dual-pane + content text surface for browse. pub(crate) use crate::tui::content::io::{ search_match_half, ContentIoFocus, ContentIoFrame, ContentIoTexts, ContentScrolls, + ExpandedBlocks, +}; +pub(crate) use crate::tui::content::text::{ + structure_block_index, workspace_content_io_texts_expanded, workspace_content_text, }; -pub(crate) use crate::tui::content::text::{workspace_content_io_texts, workspace_content_text}; #[cfg(test)] mod tests; diff --git a/src/tui/workspace/model.rs b/src/tui/workspace/model.rs index b1c73ebe..75faf582 100644 --- a/src/tui/workspace/model.rs +++ b/src/tui/workspace/model.rs @@ -8,8 +8,10 @@ use std::collections::HashSet; use std::time::SystemTime; use crate::commands::select::CommandSelection; -use crate::tui::content::io::{ContentIoFocus, ContentIoFrame, ContentIoTexts, ContentScrolls}; -use crate::tui::content::text::{content_io_from_record, structured_part_text}; +use crate::tui::content::io::{ + ContentIoFocus, ContentIoFrame, ContentIoTexts, ContentScrolls, ExpandedBlocks, +}; +use crate::tui::content::text::{content_io_from_record_expanded, structured_part_text}; use crate::tui::content::view::{ContentSelection, ContentViewMode}; use crate::tui::search::WorkspaceSearchScope; use crate::tui::theme; @@ -228,11 +230,22 @@ impl WorkspaceDialogue { self.content_io_texts(mode, target).join_displayed() } - /// Input / Output bodies for the dual content panes (no section headers). + /// Input / Output bodies for the dual content panes (default expand state). pub(crate) fn content_io_texts( &self, mode: ContentViewMode, target: Option, + ) -> ContentIoTexts { + self.content_io_texts_expanded(mode, target, &ExpandedBlocks::default()) + } + + /// Input / Output bodies for the dual content panes with per-block + /// expansion (read mode shows full blocks for the listed indices). + pub(crate) fn content_io_texts_expanded( + &self, + mode: ContentViewMode, + target: Option, + expanded: &ExpandedBlocks, ) -> ContentIoTexts { if let Some(target @ WorkAt::Part(_)) = target { let text = match mode { @@ -279,7 +292,7 @@ impl WorkspaceDialogue { }; } let reading = matches!(mode, ContentViewMode::Reading); - content_io_from_record(record, reading) + content_io_from_record_expanded(record, reading, expanded) } pub(crate) fn content_ref(&self, target: Option) -> Option { From 399592c3fe928e7ddd9b161c6d8e1e06934acf4d Mon Sep 17 00:00:00 2001 From: Ariestar <1941137088@qq.com> Date: Thu, 13 Aug 2026 15:37:13 +0800 Subject: [PATCH 02/10] refactor(tui): merge _expanded wrappers into single io-texts functions content_io_from_record / content_io_texts / workspace_content_io_texts now take expanded: &ExpandedBlocks directly instead of a default-state wrapper pair; ContentCtx borrows the expansion state per frame instead of cloning. Shared is_structure_marker() helper replaces inline starts_with checks. --- src/commands/browse/help.rs | 8 +++---- src/commands/browse/panes.rs | 10 ++++---- src/commands/browse/picker.rs | 2 +- src/tui/content/markdown.rs | 2 +- src/tui/content/text.rs | 45 +++++++++++++---------------------- src/tui/content/view.rs | 2 +- src/tui/workspace/mod.rs | 2 +- src/tui/workspace/model.rs | 18 ++++---------- src/tui/workspace/tests.rs | 7 ++++++ 9 files changed, 42 insertions(+), 54 deletions(-) diff --git a/src/commands/browse/help.rs b/src/commands/browse/help.rs index deaf40bb..ebf5cbbe 100644 --- a/src/commands/browse/help.rs +++ b/src/commands/browse/help.rs @@ -8,9 +8,9 @@ use ratatui::widgets::ListState; use crate::tui::content::view::ContentViewMode; use crate::tui::terminal::suspend; use crate::tui::workspace::{ - can_open_dialogue_vim, selected_index, workspace_content_io_texts_expanded, - workspace_content_text, workspace_layout, ContentIoFocus, ContentIoFrame, ContentScrolls, - ExpandedBlocks, WorkspaceDialogue, WorkspaceFocus, WorkspaceHelpAction, WorkspacePickedContent, + can_open_dialogue_vim, selected_index, workspace_content_io_texts, workspace_content_text, + workspace_layout, ContentIoFocus, ContentIoFrame, ContentScrolls, ExpandedBlocks, + WorkspaceDialogue, WorkspaceFocus, WorkspaceHelpAction, WorkspacePickedContent, WorkspaceSession, WorkspaceSource, }; use sivtr_core::record::WorkAt; @@ -248,7 +248,7 @@ pub(super) fn apply_workspace_help_action( *focus, *fullscreen, ); - let io = workspace_content_io_texts_expanded( + let io = workspace_content_io_texts( dialogues, selected_dialogues, dialogue_idx, diff --git a/src/commands/browse/panes.rs b/src/commands/browse/panes.rs index e99fe06b..7728ee54 100644 --- a/src/commands/browse/panes.rs +++ b/src/commands/browse/panes.rs @@ -10,8 +10,8 @@ use crate::pane::{Pane, PaneInput, SlidingPane, WindowRow}; use crate::tui::content::view::ContentViewMode; use crate::tui::workspace::{ - workspace_content_io_texts_expanded, ContentIoFocus, ContentIoFrame, ContentIoTexts, - ExpandedBlocks, WorkspaceDialogue, WorkspaceSession, WorkspaceSource, + workspace_content_io_texts, ContentIoFocus, ContentIoFrame, ContentIoTexts, ExpandedBlocks, + WorkspaceDialogue, WorkspaceSession, WorkspaceSource, }; use sivtr_core::ai::AgentSelection; use sivtr_core::record::{WorkAt, WorkRecord, WorkRef}; @@ -419,7 +419,7 @@ pub struct ContentCtx<'a> { pub target: Option, pub area: ratatui::layout::Rect, pub io_focus: ContentIoFocus, - pub expanded: ExpandedBlocks, + pub expanded: &'a ExpandedBlocks, } /// Tracks layout line counts for Input / Output halves separately. @@ -439,13 +439,13 @@ impl ContentPane { /// Build texts + dynamic layout metrics for this frame. pub fn ensure(&mut self, ctx: ContentCtx<'_>) -> ContentIoTexts { - let texts = workspace_content_io_texts_expanded( + let texts = workspace_content_io_texts( ctx.dialogues, ctx.selected_dialogues, ctx.highlighted_idx, ctx.mode, ctx.target, - &ctx.expanded, + ctx.expanded, ); let frame = ContentIoFrame::build(ctx.area, texts, ctx.mode, ctx.io_focus); self.input_lines = frame.input_lines; diff --git a/src/commands/browse/picker.rs b/src/commands/browse/picker.rs index c8aed839..03ac463a 100644 --- a/src/commands/browse/picker.rs +++ b/src/commands/browse/picker.rs @@ -344,7 +344,7 @@ pub(crate) fn run( target: active_content_at, area: layout.content, io_focus: content_io_focus, - expanded: expanded_blocks.clone(), + expanded: &expanded_blocks, }), content_mode, content_io_focus, diff --git a/src/tui/content/markdown.rs b/src/tui/content/markdown.rs index 437f05ce..b271e039 100644 --- a/src/tui/content/markdown.rs +++ b/src/tui/content/markdown.rs @@ -814,7 +814,7 @@ fn agent_heading_style(text: &str) -> Option