feat: more native menus with expected reactions
This commit is contained in:
@@ -61,6 +61,7 @@ pub(crate) struct ChatMessage {
|
||||
pub(super) reasoning_open: bool,
|
||||
pub(super) content: String,
|
||||
pub(super) markdown: markdown::Content,
|
||||
pub(super) transcript: text_editor::Content,
|
||||
pub(super) a2ui_lines_processed: usize,
|
||||
pub(super) a2ui_errors: Vec<String>,
|
||||
pub(super) a2ui_replies: Vec<serde_json::Value>,
|
||||
@@ -89,14 +90,17 @@ impl ChatMessage {
|
||||
}
|
||||
|
||||
pub(super) fn refresh_markdown(&mut self) {
|
||||
let visible = crate::agent::visible_content(&self.content);
|
||||
let visible = crate::a2ui::transcript_fallback(visible);
|
||||
let content = if self.reasoning.is_some() {
|
||||
visible.trim_start()
|
||||
} else {
|
||||
&visible
|
||||
};
|
||||
if self.transcript.text() != content {
|
||||
self.transcript = text_editor::Content::with_text(content);
|
||||
}
|
||||
if !self.user && !self.tool {
|
||||
let visible = crate::agent::visible_content(&self.content);
|
||||
let visible = crate::a2ui::transcript_fallback(visible);
|
||||
let content = if self.reasoning.is_some() {
|
||||
visible.trim_start()
|
||||
} else {
|
||||
&visible
|
||||
};
|
||||
self.markdown = markdown::Content::parse(content);
|
||||
}
|
||||
}
|
||||
@@ -165,6 +169,7 @@ impl From<StoredMessage> for ChatMessage {
|
||||
reasoning_open: false,
|
||||
content: message.content,
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
a2ui_errors: Vec::new(),
|
||||
a2ui_replies: Vec::new(),
|
||||
@@ -341,6 +346,7 @@ impl App {
|
||||
return;
|
||||
}
|
||||
self.a2ui_auto_switch_pending = true;
|
||||
self.context_notice = None;
|
||||
#[cfg(target_os = "macos")]
|
||||
self.tool_cards.clear();
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -633,6 +639,9 @@ impl App {
|
||||
match active.events.try_recv() {
|
||||
Ok(GenerationEvent::Loading) => {}
|
||||
Ok(GenerationEvent::Activity(activity)) => {
|
||||
if activity.starts_with("Rebuilding context:") {
|
||||
self.context_notice = Some(activity.to_owned());
|
||||
}
|
||||
self.activity = Some(activity.into());
|
||||
}
|
||||
Ok(GenerationEvent::Compacted(_)) => {
|
||||
@@ -1666,6 +1675,7 @@ mod tests {
|
||||
reasoning_open: false,
|
||||
content: content.to_owned(),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
a2ui_errors: Vec::new(),
|
||||
a2ui_replies: Vec::new(),
|
||||
@@ -1727,6 +1737,7 @@ mod tests {
|
||||
content: "### Core / Setup\n\n| File | Lines |\n|---|---:|\n| `src/app.rs` | **1,750** |\n| `src/engine.rs` | 2,400 |\n\n### Summary\n\nDone."
|
||||
.to_owned(),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
a2ui_errors: Vec::new(),
|
||||
a2ui_replies: Vec::new(),
|
||||
@@ -1792,6 +1803,7 @@ mod tests {
|
||||
reasoning_open: false,
|
||||
content: format!("message {id}"),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
a2ui_errors: Vec::new(),
|
||||
a2ui_replies: Vec::new(),
|
||||
@@ -1829,6 +1841,7 @@ mod tests {
|
||||
reasoning_open: false,
|
||||
content: format!("message {id}"),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
a2ui_errors: Vec::new(),
|
||||
a2ui_replies: Vec::new(),
|
||||
|
||||
@@ -175,6 +175,7 @@ impl App {
|
||||
self.remember_project(project_id);
|
||||
self.selected_session = None;
|
||||
self.conversation.clear();
|
||||
self.context_notice = None;
|
||||
self.clear_a2ui();
|
||||
self.composer.clear();
|
||||
self.queued_inputs.clear();
|
||||
@@ -188,6 +189,7 @@ impl App {
|
||||
pub(super) fn discard_session(&mut self, project_id: i32) {
|
||||
if self.drafts.remove(&project_id).is_some() && self.draft_selected(project_id) {
|
||||
self.conversation.clear();
|
||||
self.context_notice = None;
|
||||
self.clear_a2ui();
|
||||
self.composer.clear();
|
||||
self.queued_inputs.clear();
|
||||
|
||||
@@ -53,16 +53,46 @@ const TRAFFIC_LIGHT_WIDTH: f32 = 78.0;
|
||||
|
||||
impl App {
|
||||
pub(crate) fn view(&self, id: window::Id) -> Element<'_, Message> {
|
||||
if self.model_manager_window == Some(id) {
|
||||
let content = if self.model_manager_window == Some(id) {
|
||||
self.model_manager()
|
||||
} else if self.help_window == Some(id) {
|
||||
self.help_view()
|
||||
} else {
|
||||
let content = self.main_view();
|
||||
#[cfg(target_os = "macos")]
|
||||
return crate::native_edit::native_edit(content, self.native_edit_commands.clone())
|
||||
.into();
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
content
|
||||
}
|
||||
self.main_view()
|
||||
};
|
||||
#[cfg(target_os = "macos")]
|
||||
return crate::native_edit::native_edit(
|
||||
content,
|
||||
self.native_edit_commands.clone(),
|
||||
self.native_edit_availability.clone(),
|
||||
)
|
||||
.into();
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
content
|
||||
}
|
||||
|
||||
fn help_view(&self) -> Element<'_, Message> {
|
||||
container(
|
||||
scrollable(
|
||||
container(
|
||||
markdown::view(
|
||||
self.help_markdown.items(),
|
||||
markdown::Settings::with_text_size(
|
||||
15,
|
||||
markdown::Style::from_palette(app_theme().palette()),
|
||||
),
|
||||
)
|
||||
.map(Message::OpenLink),
|
||||
)
|
||||
.max_width(760)
|
||||
.padding(32),
|
||||
)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.center_x(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.style(sidebar_style)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Whether a dialog covers the window. Focus moves through the whole widget
|
||||
|
||||
@@ -55,7 +55,6 @@ impl App {
|
||||
.spacing(8),
|
||||
);
|
||||
} else {
|
||||
let markdown_style = markdown::Style::from_palette(app_theme().palette());
|
||||
for (index, message) in self.conversation.iter().enumerate() {
|
||||
if message.compaction {
|
||||
messages = messages.push(
|
||||
@@ -124,22 +123,17 @@ impl App {
|
||||
}
|
||||
}
|
||||
if !message.content.is_empty() {
|
||||
if message.user || message.tool || message.markdown.items().is_empty() {
|
||||
let content = if message.reasoning.is_some() {
|
||||
crate::agent::visible_content(&message.content).trim_start()
|
||||
} else {
|
||||
crate::agent::visible_content(&message.content)
|
||||
};
|
||||
body = body.push(text(content).size(14));
|
||||
} else {
|
||||
body = body.push(
|
||||
markdown::view(
|
||||
message.markdown.items(),
|
||||
markdown::Settings::with_text_size(14, markdown_style),
|
||||
)
|
||||
.map(Message::OpenLink),
|
||||
);
|
||||
}
|
||||
body = body.push(
|
||||
text_editor(&message.transcript)
|
||||
.id(iced::widget::Id::from(format!(
|
||||
"transcript-{}-{index}",
|
||||
message.id
|
||||
)))
|
||||
.on_action(move |action| Message::TranscriptAction(index, action))
|
||||
.padding(0)
|
||||
.size(14)
|
||||
.style(selectable_text_style),
|
||||
);
|
||||
} else if active && message.reasoning.is_none() {
|
||||
body = body.push(
|
||||
text(self.activity.as_deref().unwrap_or("Loading model…")).size(14),
|
||||
@@ -292,6 +286,20 @@ impl App {
|
||||
.spacing(6)
|
||||
.align_y(Alignment::Center),
|
||||
);
|
||||
if let Some(notice) = &self.context_notice {
|
||||
messages = messages.push(
|
||||
container(
|
||||
column![
|
||||
text("Context rebuild").size(11),
|
||||
text(notice).size(13).color(muted_text()),
|
||||
]
|
||||
.spacing(5),
|
||||
)
|
||||
.padding(14)
|
||||
.width(Length::Fill)
|
||||
.style(preference_group_style),
|
||||
);
|
||||
}
|
||||
let transcript = scrollable(messages)
|
||||
.id(chat_scroll_id())
|
||||
.height(Length::Fill);
|
||||
@@ -337,6 +345,19 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn selectable_text_style(
|
||||
theme: &Theme,
|
||||
_status: iced::widget::text_editor::Status,
|
||||
) -> iced::widget::text_editor::Style {
|
||||
iced::widget::text_editor::Style {
|
||||
background: Background::Color(Color::TRANSPARENT),
|
||||
border: Border::default(),
|
||||
placeholder: muted_text(),
|
||||
value: theme.palette().text,
|
||||
selection: theme.extended_palette().primary.weak.color,
|
||||
}
|
||||
}
|
||||
|
||||
fn generation_summary(stats: crate::app::generation::GenerationStats) -> Element<'static, Message> {
|
||||
column![
|
||||
row![
|
||||
|
||||
Reference in New Issue
Block a user