fix(ai): never route a pristine-starter canvas into direct-modify
A fresh from-scratch canvas holds only the blank starter frame, and that frame ships selected. A whole-new-design request phrased as a bare noun phrase (no page/screen noun, no create verb — e.g. "Luxury webapp for managing barbershop clients") slips past both new-screen exemption gates, and the selected starter makes selected_target_instruction true, so the request was launched as a modify turn on a near-empty canvas — the model then emits an unparseable fragment and the user sees "Could not parse design nodes". Guard should_launch_direct_modify: a canvas that is only the blank starter frame has nothing real to modify, so any request on it is NEW (desktop parity of web_chat_standard's page_children_empty => New).
This commit is contained in:
parent
768ea37d7b
commit
ed50b1b5b8
|
|
@ -234,6 +234,16 @@ pub fn launch_if_pending(
|
|||
}
|
||||
|
||||
fn should_launch_direct_modify(state: &EditorState, user_text: &str) -> bool {
|
||||
// A pristine "from-scratch" canvas holds only the blank starter frame:
|
||||
// there is nothing real to modify, so ANY design request on it is a NEW
|
||||
// design, never a modify — even when the starter frame is selected and the
|
||||
// prompt is a bare noun phrase the new-screen gates don't recognize
|
||||
// (measured: "Luxury webapp for managing barbershop clients" on a fresh
|
||||
// canvas fell into run_modify_turn → glm flat-nodes → empty `"`). This is
|
||||
// the desktop parity of web_chat_standard's `page_children_empty => New`.
|
||||
if active_page_is_blank_starter_frame(state) {
|
||||
return false;
|
||||
}
|
||||
// A whole-screen draw request ("继续画一下 search 页面") must reach the
|
||||
// design pipeline's new-frame route, never get hijacked into editing the
|
||||
// existing frame in place — even when it also trips the modify classifier.
|
||||
|
|
|
|||
|
|
@ -35,6 +35,39 @@ fn state_with_selected_card() -> EditorState {
|
|||
state
|
||||
}
|
||||
|
||||
fn state_with_selected_header() -> EditorState {
|
||||
let mut state = EditorState::new();
|
||||
state.active_children_mut().clear();
|
||||
state.active_children_mut().push(frame(
|
||||
"screen",
|
||||
"Client Dashboard",
|
||||
vec![frame("header", "Header", Vec::new())],
|
||||
));
|
||||
state.set_single_selection(op_editor_core::NodeId::new("header"));
|
||||
state
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blank_starter_canvas_never_launches_modify() {
|
||||
let state = EditorState::starter();
|
||||
|
||||
assert!(!state.selection.is_empty(), "starter frame is selected");
|
||||
assert!(
|
||||
!should_launch_direct_modify(&state, "Luxury webapp for managing barbershop clients"),
|
||||
"fresh blank starter canvas has no real target to modify"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_content_modify_request_still_launches_modify() {
|
||||
let state = state_with_selected_header();
|
||||
|
||||
assert!(
|
||||
should_launch_direct_modify(&state, "change the header color to blue"),
|
||||
"selected real content + clear edit request should still update in place"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selection_with_keywordless_instruction_launches_direct_modify() {
|
||||
let state = state_with_selected_card();
|
||||
|
|
|
|||
Loading…
Reference in a new issue