Show approval controls for blocked file edits

This commit is contained in:
Ryan Ward
2026-08-25 09:43:09 -05:00
parent 43d9956a61
commit b29f35e0fb
2 changed files with 62 additions and 7 deletions
@@ -395,6 +395,20 @@ impl CodeDiffState {
fn is_waiting_for_user(&self) -> bool { fn is_waiting_for_user(&self) -> bool {
matches!(self, CodeDiffState::WaitingForUser) matches!(self, CodeDiffState::WaitingForUser)
} }
fn sync_with_action_status(&mut self, status: Option<&AIActionStatus>) {
match status {
Some(AIActionStatus::Blocked) => *self = CodeDiffState::WaitingForUser,
Some(status) if status.is_cancelled() => *self = CodeDiffState::Rejected,
Some(
AIActionStatus::Preprocessing
| AIActionStatus::Queued
| AIActionStatus::RunningAsync
| AIActionStatus::Finished(_),
)
| None => {}
}
}
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@@ -676,13 +690,21 @@ impl CodeDiffView {
ctx: &mut ViewContext<Self>, ctx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
let is_passive = model.request_type(ctx).is_passive_code_diff(); let is_passive = model.request_type(ctx).is_passive_code_diff();
let initial_state = if action_model.as_ref(ctx).is_view_only() { let mut initial_state = if action_model.as_ref(ctx).is_view_only() {
CodeDiffState::ViewOnly { is_complete: false } CodeDiffState::ViewOnly { is_complete: false }
} else if model.is_first_action_in_output(action_id, ctx) { } else if model.is_first_action_in_output(action_id, ctx) {
CodeDiffState::WaitingForUser CodeDiffState::WaitingForUser
} else { } else {
CodeDiffState::Queued CodeDiffState::Queued
}; };
if !action_model.as_ref(ctx).is_view_only() {
if let Some(conversation_id) = identifiers.client_conversation_id {
let status = action_model
.as_ref(ctx)
.get_action_status(conversation_id, action_id);
initial_state.sync_with_action_status(status.as_ref());
}
}
let view = Self::build( let view = Self::build(
action_id, action_id,
@@ -700,6 +722,18 @@ impl CodeDiffView {
ctx.subscribe_to_model( ctx.subscribe_to_model(
&action_model, &action_model,
move |me, action_model, event, ctx| match event { move |me, action_model, event, ctx| match event {
BlocklistAIActionEvent::ActionBlockedOnUserConfirmation {
action_id: event_action_id,
conversation_id: event_conversation_id,
..
} if !me.is_complete()
&& !matches!(me.state, CodeDiffState::ViewOnly { .. })
&& *event_action_id == me.action_id
&& me.identifiers.client_conversation_id == Some(*event_conversation_id) =>
{
me.state = CodeDiffState::WaitingForUser;
ctx.notify();
}
BlocklistAIActionEvent::FinishedAction { BlocklistAIActionEvent::FinishedAction {
action_id: event_action_id, action_id: event_action_id,
conversation_id: event_conversation_id, conversation_id: event_conversation_id,
@@ -715,18 +749,16 @@ impl CodeDiffView {
.as_ref(ctx) .as_ref(ctx)
.get_action_status(conversation_id, &me.action_id) .get_action_status(conversation_id, &me.action_id)
{ {
Some(AIActionStatus::Blocked) => {
me.state = CodeDiffState::WaitingForUser;
ctx.notify();
}
Some(status) => { Some(status) => {
if matches!(me.state, CodeDiffState::ViewOnly { .. }) if matches!(me.state, CodeDiffState::ViewOnly { .. })
&& status.is_success() && status.is_success()
{ {
me.state = CodeDiffState::ViewOnly { is_complete: true }; me.state = CodeDiffState::ViewOnly { is_complete: true };
me.should_expand_when_complete = false; me.should_expand_when_complete = false;
} else if status.is_cancelled() { } else {
me.state = CodeDiffState::Rejected; me.state.sync_with_action_status(Some(&status));
}
if status.is_cancelled() {
me.should_expand_when_complete = false; me.should_expand_when_complete = false;
} }
ctx.notify(); ctx.notify();
@@ -3268,3 +3300,7 @@ fn editor_range_to_file_context_range(range: Range<usize>) -> Range<usize> {
fn file_context_range_to_editor_range(range: Range<usize>) -> Range<usize> { fn file_context_range_to_editor_range(range: Range<usize>) -> Range<usize> {
range.start.saturating_sub(1)..range.end.saturating_sub(1) range.start.saturating_sub(1)..range.end.saturating_sub(1)
} }
#[cfg(test)]
#[path = "code_diff_view_tests.rs"]
mod tests;
@@ -0,0 +1,19 @@
use super::*;
#[test]
fn blocked_action_status_makes_queued_diff_actionable() {
let mut state = CodeDiffState::Queued;
state.sync_with_action_status(Some(&AIActionStatus::Blocked));
assert!(state.is_waiting_for_user());
}
#[test]
fn preprocessing_action_status_keeps_diff_queued() {
let mut state = CodeDiffState::Queued;
state.sync_with_action_status(Some(&AIActionStatus::Preprocessing));
assert!(matches!(state, CodeDiffState::Queued));
}