feat(web): hide desktop-only entry points on the web host
The image Search/Generate buttons, the six MCP CLI-integration toggles, and the auto-update card all painted on web with no executor behind them — clicking did nothing (silent dead UI, worse than a missing feature). Gate each behind a cfg const following the existing GIT_BUTTON_AVAILABLE pattern, with paint, hit-test, and layout heights kept in sync; the experimental-features card moves up into the hidden auto-update slot.
This commit is contained in:
parent
47ff290cf6
commit
8f1be67ab4
|
|
@ -147,6 +147,13 @@ fn client_config_copy_button_rect(content: Rect) -> Rect {
|
|||
}
|
||||
}
|
||||
|
||||
/// Host capability: the CLI-integration toggles write MCP endpoints
|
||||
/// into external CLI config files (`~/.claude.json` etc.) via the
|
||||
/// desktop MCP runtime (`mcp_integrations.rs`). The web host has no
|
||||
/// consumer — hide the grid there instead of painting toggles that
|
||||
/// silently do nothing (same pattern as `GIT_BUTTON_AVAILABLE`).
|
||||
pub(super) const CLI_INTEGRATIONS_AVAILABLE: bool = !cfg!(target_arch = "wasm32");
|
||||
|
||||
pub fn hit_test(content: Rect, settings: &AgentSettings, scrolled: Point2D) -> McpHit {
|
||||
if (server_button_rect(content)).contains(scrolled) {
|
||||
return McpHit::ToggleServer;
|
||||
|
|
@ -157,9 +164,11 @@ pub fn hit_test(content: Rect, settings: &AgentSettings, scrolled: Point2D) -> M
|
|||
if !settings.mcp_server.running && (port_field_rect(content)).contains(scrolled) {
|
||||
return McpHit::FocusPort;
|
||||
}
|
||||
for (i, cli) in McpCli::ALL.iter().enumerate() {
|
||||
if (cli_cell_rect(content, settings, i)).contains(scrolled) {
|
||||
return McpHit::ToggleCli(*cli);
|
||||
if CLI_INTEGRATIONS_AVAILABLE {
|
||||
for (i, cli) in McpCli::ALL.iter().enumerate() {
|
||||
if (cli_cell_rect(content, settings, i)).contains(scrolled) {
|
||||
return McpHit::ToggleCli(*cli);
|
||||
}
|
||||
}
|
||||
}
|
||||
McpHit::None
|
||||
|
|
@ -187,6 +196,11 @@ pub(super) fn paint_mcp_tab(
|
|||
paint_server_card(cx, theme, settings, ui, content, now_ms);
|
||||
paint_client_config(cx, theme, settings, ui, content, now_ms);
|
||||
|
||||
// Terminal-integrations section — desktop-only (see
|
||||
// `CLI_INTEGRATIONS_AVAILABLE`).
|
||||
if !CLI_INTEGRATIONS_AVAILABLE {
|
||||
return;
|
||||
}
|
||||
let mut y =
|
||||
server_card_top(content) + SERVER_CARD_H + client_config_block_h(settings) + SECTION_GAP;
|
||||
let section_title = TextLayout::single_run(
|
||||
|
|
|
|||
|
|
@ -37,7 +37,18 @@ fn auto_update_card_rect(content: Rect) -> Rect {
|
|||
}
|
||||
}
|
||||
|
||||
/// Host capability: the auto-update toggle drives the desktop
|
||||
/// updater (`op-host-desktop/src/update_check.rs`). The web host has
|
||||
/// no updater — hide the card there instead of painting a switch that
|
||||
/// silently does nothing (same pattern as `GIT_BUTTON_AVAILABLE`).
|
||||
pub(super) const AUTO_UPDATE_AVAILABLE: bool = !cfg!(target_arch = "wasm32");
|
||||
|
||||
fn experimental_card_rect(content: Rect) -> Rect {
|
||||
if !AUTO_UPDATE_AVAILABLE {
|
||||
// The auto-update card is hidden — the experimental card
|
||||
// moves up into its slot.
|
||||
return auto_update_card_rect(content);
|
||||
}
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
content.origin.x,
|
||||
|
|
@ -60,7 +71,7 @@ fn switch_rect_for(card: Rect) -> Rect {
|
|||
}
|
||||
|
||||
pub fn hit_test(content: Rect, scrolled: Point2D) -> SystemHit {
|
||||
if switch_rect_for(auto_update_card_rect(content)).contains(scrolled) {
|
||||
if AUTO_UPDATE_AVAILABLE && switch_rect_for(auto_update_card_rect(content)).contains(scrolled) {
|
||||
return SystemHit::ToggleAutoUpdate;
|
||||
}
|
||||
if switch_rect_for(experimental_card_rect(content)).contains(scrolled) {
|
||||
|
|
@ -88,15 +99,18 @@ pub(super) fn paint_system_tab(
|
|||
Point2D::new(content.origin.x, content.origin.y + 20.0),
|
||||
);
|
||||
|
||||
paint_toggle_card(
|
||||
cx,
|
||||
theme,
|
||||
ui,
|
||||
auto_update_card_rect(content),
|
||||
"agents.autoUpdate",
|
||||
"settings.autoUpdateDesc",
|
||||
settings.auto_update_enabled,
|
||||
);
|
||||
// Auto-update card — desktop-only (see `AUTO_UPDATE_AVAILABLE`).
|
||||
if AUTO_UPDATE_AVAILABLE {
|
||||
paint_toggle_card(
|
||||
cx,
|
||||
theme,
|
||||
ui,
|
||||
auto_update_card_rect(content),
|
||||
"agents.autoUpdate",
|
||||
"settings.autoUpdateDesc",
|
||||
settings.auto_update_enabled,
|
||||
);
|
||||
}
|
||||
paint_toggle_card(
|
||||
cx,
|
||||
theme,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ use crate::widgets::property_panel_inputs::{
|
|||
use crate::widgets::PaintCx;
|
||||
use crate::{Point2D, Rect, TextLayout};
|
||||
|
||||
/// Host capability: the Search / Generate buttons drive the image
|
||||
/// search + generation sessions that only the desktop host executes
|
||||
/// (`image_search_session.rs` / `image_generate_host.rs`). The web
|
||||
/// host has no drain for them — hide the buttons there instead of
|
||||
/// painting dead UI (same pattern as `GIT_BUTTON_AVAILABLE`).
|
||||
pub(super) const IMAGE_REMOTE_ACTIONS_AVAILABLE: bool = !cfg!(target_arch = "wasm32");
|
||||
|
||||
/// Total height the image section consumes BEFORE the trailing
|
||||
/// `SECTION_GAP` the walkers add — header + thumb row + optional
|
||||
/// warning + buttons row + the legacy 34 px divider tail.
|
||||
|
|
@ -28,11 +35,15 @@ pub fn image_section_height(has_warning: bool) -> f32 {
|
|||
} else {
|
||||
0.0
|
||||
};
|
||||
let buttons = if IMAGE_REMOTE_ACTIONS_AVAILABLE {
|
||||
IMAGE_ROW_GAP + IMAGE_BUTTON_H
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
crate::widgets::property_panel_inputs::SECTION_HEADER_HEIGHT
|
||||
+ INPUT_HEIGHT
|
||||
+ warning
|
||||
+ IMAGE_ROW_GAP
|
||||
+ IMAGE_BUTTON_H
|
||||
+ buttons
|
||||
+ 34.0
|
||||
}
|
||||
|
||||
|
|
@ -70,22 +81,24 @@ pub fn push_image_action_rects(
|
|||
));
|
||||
y += IMAGE_WARNING_H;
|
||||
}
|
||||
y += IMAGE_ROW_GAP;
|
||||
let half_w = (usable_w - 4.0) / 2.0;
|
||||
out.push((
|
||||
PropertyPanelAction::ToggleImageSearchPopover,
|
||||
Rect {
|
||||
origin: Point2D::new(x0 + PAD_X, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
));
|
||||
out.push((
|
||||
PropertyPanelAction::ToggleImageGeneratePopover,
|
||||
Rect {
|
||||
origin: Point2D::new(x0 + PAD_X + half_w + 4.0, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
));
|
||||
if IMAGE_REMOTE_ACTIONS_AVAILABLE {
|
||||
y += IMAGE_ROW_GAP;
|
||||
let half_w = (usable_w - 4.0) / 2.0;
|
||||
out.push((
|
||||
PropertyPanelAction::ToggleImageSearchPopover,
|
||||
Rect {
|
||||
origin: Point2D::new(x0 + PAD_X, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
));
|
||||
out.push((
|
||||
PropertyPanelAction::ToggleImageGeneratePopover,
|
||||
Rect {
|
||||
origin: Point2D::new(x0 + PAD_X + half_w + 4.0, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
|
@ -160,30 +173,35 @@ pub fn paint_image_node_section(
|
|||
y = paint_warning_row(cx, theme, warning, x, y, width);
|
||||
}
|
||||
|
||||
// Search / Generate buttons (TS outline buttons, flex-1 h-7).
|
||||
y += IMAGE_ROW_GAP;
|
||||
let half_w = (usable_w - 4.0) / 2.0;
|
||||
paint_outline_button(
|
||||
cx,
|
||||
theme,
|
||||
Rect {
|
||||
origin: Point2D::new(x + PAD_X, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
Icon::Search,
|
||||
"Search",
|
||||
);
|
||||
paint_outline_button(
|
||||
cx,
|
||||
theme,
|
||||
Rect {
|
||||
origin: Point2D::new(x + PAD_X + half_w + 4.0, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
Icon::Sparkles,
|
||||
"Generate",
|
||||
);
|
||||
y += IMAGE_BUTTON_H + 34.0;
|
||||
// Search / Generate buttons (TS outline buttons, flex-1 h-7) —
|
||||
// desktop-only, mirrored by `image_section_height` /
|
||||
// `push_image_action_rects` so paint + hit-test stay in sync.
|
||||
if IMAGE_REMOTE_ACTIONS_AVAILABLE {
|
||||
y += IMAGE_ROW_GAP;
|
||||
let half_w = (usable_w - 4.0) / 2.0;
|
||||
paint_outline_button(
|
||||
cx,
|
||||
theme,
|
||||
Rect {
|
||||
origin: Point2D::new(x + PAD_X, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
Icon::Search,
|
||||
"Search",
|
||||
);
|
||||
paint_outline_button(
|
||||
cx,
|
||||
theme,
|
||||
Rect {
|
||||
origin: Point2D::new(x + PAD_X + half_w + 4.0, y),
|
||||
size: Point2D::new(half_w, IMAGE_BUTTON_H),
|
||||
},
|
||||
Icon::Sparkles,
|
||||
"Generate",
|
||||
);
|
||||
y += IMAGE_BUTTON_H;
|
||||
}
|
||||
y += 34.0;
|
||||
paint_section_divider(cx, theme, x, y, width);
|
||||
y + SECTION_GAP
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue