//! Fixed-coordinate renderer for the exported Penpot color overlay.

mod sliders;

use egui::{self, ecolor::Hsva};
use egui_phosphor_icons::{icons, Icon};

use super::{
    byte, color32, parse_hex, rgba, set_current, ColorPickerResponse, PickerMode, PickerSession,
    POPUP_SIZE,
};
use crate::design_system::typography::TypeRole;

const RECENTS: [egui::Color32; 8] = colors([
    0xE7D4B4, 0x53D3E6, 0xCA6CE0, 0xF06B75, 0x55C58A, 0xF3C85B, 0x6F88EF, 0xF3F5F7,
]);
const PRESETS: [egui::Color32; 24] = colors([
    0xE7D4B4, 0xF2EEE8, 0xD5B59C, 0xA8795B, 0x734C3C, 0x3B2D29, 0xE45D68, 0xF09A55, 0xE9D05B,
    0x79C267, 0x45B7A6, 0x4FA9D8, 0x5A78DE, 0x8E68D8, 0xC26BC7, 0xE68DB5, 0xF5F7FA, 0xC4CCD5,
    0x8995A3, 0x56616E, 0x303842, 0x1D242C, 0x101419, 0x07090C,
]);
const PLANNED_EYEDROPPER: &str = "Planned — screen sampling pending";

const fn colors<const N: usize>(values: [u32; N]) -> [egui::Color32; N] {
    let mut result = [egui::Color32::BLACK; N];
    let mut index = 0;
    while index < N {
        let value = values[index];
        result[index] = egui::Color32::from_rgb(
            ((value >> 16) & 0xFF) as u8,
            ((value >> 8) & 0xFF) as u8,
            (value & 0xFF) as u8,
        );
        index += 1;
    }
    result
}

struct Geometry;

impl Geometry {
    const HEADER_HEIGHT: f32 = 38.0;
    const CONTENT: egui::Rect =
        egui::Rect::from_min_max(egui::pos2(12.0, 74.0), egui::pos2(408.0, 276.0));
    const WHEEL_EDITOR: egui::Rect =
        egui::Rect::from_min_max(egui::pos2(12.0, 74.0), egui::pos2(190.0, 276.0));
    const VALUES: egui::Rect =
        egui::Rect::from_min_max(egui::pos2(198.0, 82.0), egui::pos2(396.0, 268.0));
    const RECENTS: egui::Rect =
        egui::Rect::from_min_max(egui::pos2(12.0, 282.0), egui::pos2(408.0, 310.0));
    const FOOTER: egui::Rect =
        egui::Rect::from_min_max(egui::pos2(12.0, 316.0), egui::pos2(408.0, 342.0));

    fn rect(root: egui::Rect, local: egui::Rect) -> egui::Rect {
        local.translate(root.min.to_vec2())
    }

    fn at(root: egui::Rect, x: f32, y: f32, width: f32, height: f32) -> egui::Rect {
        egui::Rect::from_min_size(root.min + egui::vec2(x, y), egui::vec2(width, height))
    }
}

pub(super) fn show(
    ui: &mut egui::Ui,
    label: &str,
    session: &mut PickerSession,
    result: &mut ColorPickerResponse,
) -> egui::Response {
    let palette = crate::design_system::palette(ui);
    let (root, response) = ui.allocate_exact_size(POPUP_SIZE, egui::Sense::hover());
    ui.painter().rect(
        root,
        7.0,
        palette.panel,
        egui::Stroke::new(1.0_f32, palette.border_strong),
        egui::StrokeKind::Inside,
    );
    ui.painter().line_segment(
        [
            root.min + egui::vec2(0.0, Geometry::HEADER_HEIGHT),
            root.min + egui::vec2(root.width(), Geometry::HEADER_HEIGHT),
        ],
        egui::Stroke::new(1.0_f32, palette.border),
    );

    header(ui, root, label, session, result);
    mode_tabs(ui, root, session);
    intensity_control(ui, root, session);
    match session.mode {
        PickerMode::Wheel => wheel_mode(ui, root, session),
        PickerMode::Sliders => sliders::show(ui, root, session),
        PickerMode::Presets => presets_mode(ui, root, session),
    }
    recent_row(ui, root, session);
    footer(ui, root, result);
    response
}

fn intensity_control(ui: &mut egui::Ui, root: egui::Rect, session: &mut PickerSession) {
    let Some(intensity) = session.current_intensity.as_mut() else {
        return;
    };
    let palette = crate::design_system::palette(ui);
    text(
        ui,
        root.min + egui::vec2(272.0, 55.0),
        egui::Align2::LEFT_CENTER,
        "Intensity",
        TypeRole::Small.font(),
        palette.text_muted,
    );
    ui.put(
        Geometry::at(root, 326.0, 43.0, 82.0, 24.0),
        egui::DragValue::new(intensity)
            .range(0.0..=50_000.0)
            .speed(10.0),
    )
    .on_hover_text("Emissive luminance multiplier in nits");
}

fn header(
    ui: &mut egui::Ui,
    root: egui::Rect,
    label: &str,
    session: &PickerSession,
    result: &mut ColorPickerResponse,
) {
    let palette = crate::design_system::palette(ui);
    text(
        ui,
        root.min + egui::vec2(12.0, 19.0),
        egui::Align2::LEFT_CENTER,
        label,
        TypeRole::Section.font(),
        palette.text_primary,
    );
    text(
        ui,
        root.min + egui::vec2(84.0, 19.0),
        egui::Align2::LEFT_CENTER,
        "sRGB · RGBA",
        TypeRole::Small.font(),
        palette.text_muted,
    );
    paint_swatch(
        ui,
        Geometry::at(root, 348.0, 7.0, 26.0, 24.0),
        color32(session.current),
        egui::Sense::hover(),
        "Current color",
    );
    if icon_button(
        ui,
        Geometry::at(root, 384.0, 7.0, 24.0, 24.0),
        icons::X,
        "Cancel color changes",
        true,
    )
    .clicked()
    {
        result.cancelled = true;
    }
}

fn mode_tabs(ui: &mut egui::Ui, root: egui::Rect, session: &mut PickerSession) {
    let palette = crate::design_system::palette(ui);
    for (index, (mode, icon, label, width)) in [
        (PickerMode::Wheel, icons::CIRCLE, "Wheel", 76.0),
        (PickerMode::Sliders, icons::SLIDERS, "Sliders", 76.0),
        (PickerMode::Presets, icons::SQUARES_FOUR, "Presets", 78.0),
    ]
    .into_iter()
    .enumerate()
    {
        let x = [12.0, 92.0, 172.0][index];
        let rect = Geometry::at(root, x, 42.0, width, 26.0);
        let selected = session.mode == mode;
        let response = ui.put(
            rect,
            egui::Button::new(crate::design_system::controls::icon_label(
                icon,
                label,
                TypeRole::Body,
                12.0,
                if selected {
                    palette.text_primary
                } else {
                    palette.text_secondary
                },
            ))
            .fill(if selected {
                palette.accent_dark
            } else {
                palette.control
            })
            .stroke(egui::Stroke::new(
                1.0_f32,
                if selected {
                    palette.accent
                } else {
                    palette.border
                },
            ))
            .corner_radius(4.0),
        );
        if selected {
            ui.painter().rect_filled(
                egui::Rect::from_center_size(
                    egui::pos2(rect.center().x, rect.bottom() - 1.0),
                    egui::vec2(60.0, 2.0),
                ),
                1.0,
                palette.accent,
            );
        }
        if response.clicked() {
            session.mode = mode;
        }
    }
}

fn wheel_mode(ui: &mut egui::Ui, root: egui::Rect, session: &mut PickerSession) {
    let palette = crate::design_system::palette(ui);
    let ring = Geometry::at(root, 29.0, 88.0, 144.0, 144.0);
    let sv = Geometry::at(root, 65.0, 124.0, 72.0, 72.0);
    let editor = Geometry::rect(root, Geometry::WHEEL_EDITOR);
    let response = ui.interact(
        editor,
        ui.make_persistent_id("penpot_color_wheel"),
        egui::Sense::click_and_drag(),
    );
    let mut hsva = Hsva::from_rgba_unmultiplied(
        session.current[0],
        session.current[1],
        session.current[2],
        session.current[3],
    );
    paint_hue_wheel(ui.painter(), ring, hsva.h, palette.text_primary);
    paint_sv(
        ui.painter(),
        sv,
        hsva.h,
        hsva.s,
        hsva.v,
        palette.text_primary,
    );
    if response.clicked() || response.dragged() {
        if let Some(position) = response.interact_pointer_pos() {
            let delta = position - ring.center();
            if sv.contains(position) {
                hsva.s = ((position.x - sv.left()) / sv.width()).clamp(0.0, 1.0);
                hsva.v = (1.0 - (position.y - sv.top()) / sv.height()).clamp(0.0, 1.0);
            } else if (52.0..=76.0).contains(&delta.length()) {
                hsva.h = (delta.angle() / std::f32::consts::TAU + 0.25).rem_euclid(1.0);
            }
            let [r, g, b] = hsva.to_rgb();
            set_current(session, [r, g, b, hsva.a]);
        }
    }
    text(
        ui,
        root.min + egui::vec2(101.0, 252.0),
        egui::Align2::CENTER_CENTER,
        "Drag ring or field",
        TypeRole::Small.font(),
        palette.text_muted,
    );
    values(ui, root, session);
}

fn values(ui: &mut egui::Ui, root: egui::Rect, session: &mut PickerSession) {
    let palette = crate::design_system::palette(ui);
    let _values_bounds = Geometry::rect(root, Geometry::VALUES);
    caption(ui, root, 198.0, 82.0, "CURRENT");
    caption(ui, root, 302.0, 82.0, "PREVIOUS");
    paint_swatch(
        ui,
        Geometry::at(root, 198.0, 96.0, 94.0, 30.0),
        color32(session.current),
        egui::Sense::hover(),
        "Current live value",
    );
    paint_swatch(
        ui,
        Geometry::at(root, 302.0, 96.0, 94.0, 30.0),
        color32(session.original),
        egui::Sense::hover(),
        "Value when the picker opened",
    );

    caption(ui, root, 198.0, 132.0, "HEX");
    let hex_field = Geometry::at(root, 198.0, 144.0, 166.0, 24.0);
    field_background(ui, hex_field);
    let edit_rect = egui::Rect::from_min_max(
        hex_field.min,
        egui::pos2(hex_field.right() - 24.0, hex_field.bottom()),
    );
    let edit = ui.put(
        edit_rect.shrink2(egui::vec2(5.0, 2.0)),
        egui::TextEdit::singleline(&mut session.hex)
            .font(TypeRole::Body.font())
            .frame(egui::Frame::NONE),
    );
    if edit.lost_focus() && ui.input(|input| input.key_pressed(egui::Key::Enter)) {
        if let Some(value) = parse_hex(&session.hex) {
            set_current(session, value);
        }
    }
    if bare_icon(
        ui,
        egui::Rect::from_min_size(
            egui::pos2(hex_field.right() - 24.0, hex_field.top()),
            egui::vec2(24.0, 24.0),
        ),
        icons::COPY,
        "Copy HEX",
        palette.text_secondary,
    )
    .clicked()
    {
        ui.ctx().copy_text(session.hex.clone());
    }
    let eyedropper = Geometry::at(root, 370.0, 144.0, 26.0, 24.0);
    field_background(ui, eyedropper);
    bare_icon(
        ui,
        eyedropper,
        icons::EYEDROPPER,
        PLANNED_EYEDROPPER,
        palette.text_muted,
    );

    caption(ui, root, 198.0, 174.0, "RGBA");
    let mut bytes = session.current.map(byte);
    let before_bytes = bytes;
    for (index, prefix) in ["R ", "G ", "B ", "A "].into_iter().enumerate() {
        let rect = Geometry::at(root, 198.0 + index as f32 * 49.0, 186.0, 45.0, 24.0);
        ui.put(
            rect,
            egui::DragValue::new(&mut bytes[index])
                .range(0..=255)
                .prefix(prefix),
        );
    }
    if bytes != before_bytes {
        set_current(session, bytes.map(|component| component as f32 / 255.0));
    }

    caption(ui, root, 198.0, 216.0, "HSV");
    let mut hsva = Hsva::from_rgba_unmultiplied(
        session.current[0],
        session.current[1],
        session.current[2],
        session.current[3],
    );
    let mut hsv = [hsva.h * 360.0, hsva.s * 100.0, hsva.v * 100.0];
    let before_hsv = hsv;
    for (index, suffix) in ["°", "%", "%"].into_iter().enumerate() {
        let rect = Geometry::at(root, 198.0 + index as f32 * 66.0, 228.0, 61.0, 24.0);
        ui.put(
            rect,
            egui::DragValue::new(&mut hsv[index])
                .range(if index == 0 { 0.0..=360.0 } else { 0.0..=100.0 })
                .suffix(suffix)
                .max_decimals(0),
        );
    }
    if hsv != before_hsv {
        hsva.h = hsv[0] / 360.0;
        hsva.s = hsv[1] / 100.0;
        hsva.v = hsv[2] / 100.0;
        let [r, g, b] = hsva.to_rgb();
        set_current(session, [r, g, b, hsva.a]);
    }

    caption(ui, root, 198.0, 258.0, "ALPHA");
    alpha_control(ui, Geometry::at(root, 240.0, 256.0, 156.0, 14.0), session);
}

fn presets_mode(ui: &mut egui::Ui, root: egui::Rect, session: &mut PickerSession) {
    let palette = crate::design_system::palette(ui);
    let content = Geometry::rect(root, Geometry::CONTENT);
    ui.painter().rect_filled(content, 4.0, palette.recessed);
    caption(ui, root, 24.0, 86.0, "MATERIAL PALETTE");
    for (index, color) in PRESETS.into_iter().enumerate() {
        let column = index % 8;
        let row = index / 8;
        let rect = Geometry::at(
            root,
            24.0 + column as f32 * 47.0,
            104.0 + row as f32 * 52.0,
            34.0,
            34.0,
        );
        if paint_swatch(ui, rect, color, egui::Sense::click(), "Use preset color").clicked() {
            set_current(session, rgba(color));
        }
    }
}

fn recent_row(ui: &mut egui::Ui, root: egui::Rect, session: &mut PickerSession) {
    let palette = crate::design_system::palette(ui);
    let row = Geometry::rect(root, Geometry::RECENTS);
    ui.painter().rect_filled(row, 4.0, palette.section);
    text(
        ui,
        root.min + egui::vec2(20.0, 296.0),
        egui::Align2::LEFT_CENTER,
        "RECENT",
        TypeRole::Caption.font(),
        palette.text_muted,
    );
    for (index, color) in RECENTS.into_iter().enumerate() {
        let rect = Geometry::at(root, 74.0 + index as f32 * 36.0, 286.0, 28.0, 20.0);
        if paint_swatch(ui, rect, color, egui::Sense::click(), "Use recent color").clicked() {
            set_current(session, rgba(color));
        }
    }
}

fn footer(ui: &mut egui::Ui, root: egui::Rect, result: &mut ColorPickerResponse) {
    let palette = crate::design_system::palette(ui);
    let footer = Geometry::rect(root, Geometry::FOOTER);
    text(
        ui,
        footer.left_center(),
        egui::Align2::LEFT_CENTER,
        "Enter apply · Esc cancel",
        TypeRole::Small.font(),
        palette.text_muted,
    );
    if labeled_button(
        ui,
        Geometry::at(root, 254.0, 316.0, 72.0, 26.0),
        icons::X,
        "Cancel",
        false,
    )
    .clicked()
    {
        result.cancelled = true;
    }
    if labeled_button(
        ui,
        Geometry::at(root, 334.0, 316.0, 74.0, 26.0),
        icons::CHECK,
        "Apply",
        true,
    )
    .clicked()
    {
        result.applied = true;
    }
}

fn alpha_control(ui: &mut egui::Ui, rect: egui::Rect, session: &mut PickerSession) {
    let palette = crate::design_system::palette(ui);
    checker(ui.painter(), rect, palette.control, palette.elevated);
    let response = ui.interact(
        rect.expand2(egui::vec2(0.0, 4.0)),
        ui.make_persistent_id("penpot_color_alpha"),
        egui::Sense::click_and_drag(),
    );
    if response.clicked() || response.dragged() {
        if let Some(position) = response.interact_pointer_pos() {
            let mut value = session.current;
            value[3] = ((position.x - rect.left()) / rect.width()).clamp(0.0, 1.0);
            set_current(session, value);
        }
    }
    let x = egui::lerp(rect.left()..=rect.right(), session.current[3]);
    ui.painter().rect_filled(
        egui::Rect::from_min_max(rect.min, egui::pos2(x, rect.bottom())),
        2.0,
        palette.accent.gamma_multiply(0.65),
    );
    ui.painter().rect_filled(
        egui::Rect::from_center_size(egui::pos2(x, rect.center().y), egui::vec2(10.0, 18.0)),
        3.0,
        palette.text_primary,
    );
}

fn paint_hue_wheel(
    painter: &egui::Painter,
    rect: egui::Rect,
    selected_hue: f32,
    marker_color: egui::Color32,
) {
    let center = rect.center();
    let outer = 72.0;
    let inner = 54.0;
    let mut mesh = egui::Mesh::default();
    const SEGMENTS: usize = 72;
    for segment in 0..SEGMENTS {
        let h0 = segment as f32 / SEGMENTS as f32;
        let h1 = (segment + 1) as f32 / SEGMENTS as f32;
        let a0 = (h0 - 0.25) * std::f32::consts::TAU;
        let a1 = (h1 - 0.25) * std::f32::consts::TAU;
        let start = mesh.vertices.len() as u32;
        let c0 = egui::Color32::from(Hsva::new(h0, 1.0, 1.0, 1.0));
        let c1 = egui::Color32::from(Hsva::new(h1, 1.0, 1.0, 1.0));
        for (radius, angle, color) in [
            (inner, a0, c0),
            (outer, a0, c0),
            (outer, a1, c1),
            (inner, a1, c1),
        ] {
            mesh.colored_vertex(
                center + egui::vec2(angle.cos(), angle.sin()) * radius,
                color,
            );
        }
        mesh.add_triangle(start, start + 1, start + 2);
        mesh.add_triangle(start, start + 2, start + 3);
    }
    painter.add(mesh);
    let angle = (selected_hue - 0.25) * std::f32::consts::TAU;
    let marker = center + egui::vec2(angle.cos(), angle.sin()) * 63.0;
    painter.circle_stroke(marker, 5.0, egui::Stroke::new(2.0_f32, marker_color));
}

fn paint_sv(
    painter: &egui::Painter,
    rect: egui::Rect,
    hue: f32,
    saturation: f32,
    value: f32,
    marker_color: egui::Color32,
) {
    let mut mesh = egui::Mesh::default();
    for (position, color) in [
        (rect.left_top(), egui::Color32::WHITE),
        (
            rect.right_top(),
            egui::Color32::from(Hsva::new(hue, 1.0, 1.0, 1.0)),
        ),
        (rect.right_bottom(), egui::Color32::BLACK),
        (rect.left_bottom(), egui::Color32::BLACK),
    ] {
        mesh.colored_vertex(position, color);
    }
    mesh.add_triangle(0, 1, 2);
    mesh.add_triangle(0, 2, 3);
    painter.add(mesh);
    let marker = egui::pos2(
        egui::lerp(rect.x_range(), saturation),
        egui::lerp(rect.y_range(), 1.0 - value),
    );
    painter.circle_stroke(marker, 5.0, egui::Stroke::new(2.0_f32, marker_color));
}

pub(super) fn checker(
    painter: &egui::Painter,
    rect: egui::Rect,
    dark: egui::Color32,
    light: egui::Color32,
) {
    let size = 6.0;
    let columns = (rect.width() / size).ceil() as usize;
    let rows = (rect.height() / size).ceil() as usize;
    for row in 0..rows {
        for column in 0..columns {
            let cell = egui::Rect::from_min_size(
                rect.min + egui::vec2(column as f32 * size, row as f32 * size),
                egui::vec2(size, size),
            )
            .intersect(rect);
            painter.rect_filled(
                cell,
                0.0,
                if (row + column) % 2 == 0 { dark } else { light },
            );
        }
    }
}

fn paint_swatch(
    ui: &mut egui::Ui,
    rect: egui::Rect,
    color: egui::Color32,
    sense: egui::Sense,
    tooltip: &str,
) -> egui::Response {
    let palette = crate::design_system::palette(ui);
    checker(ui.painter(), rect, palette.control, palette.elevated);
    ui.painter().rect_filled(rect.shrink(1.0), 3.0, color);
    ui.painter().rect_stroke(
        rect,
        3.0,
        egui::Stroke::new(1.0_f32, palette.border),
        egui::StrokeKind::Inside,
    );
    ui.interact(
        rect,
        ui.make_persistent_id(("color_swatch", rect.min.x.to_bits(), rect.min.y.to_bits())),
        sense,
    )
    .on_hover_text(tooltip)
}

fn icon_button(
    ui: &mut egui::Ui,
    rect: egui::Rect,
    icon: Icon,
    tooltip: &str,
    enabled: bool,
) -> egui::Response {
    let palette = crate::design_system::palette(ui);
    ui.put(
        rect,
        egui::Button::new(egui::RichText::new(icon.as_str()).font(egui::FontId::new(
            13.0,
            egui::FontFamily::Name("phosphor-regular".into()),
        )))
        .fill(palette.control)
        .stroke(egui::Stroke::new(1.0_f32, palette.border))
        .corner_radius(4.0)
        .sense(if enabled {
            egui::Sense::click()
        } else {
            egui::Sense::hover()
        }),
    )
    .on_hover_text(tooltip)
}

fn bare_icon(
    ui: &mut egui::Ui,
    rect: egui::Rect,
    icon: Icon,
    tooltip: &str,
    color: egui::Color32,
) -> egui::Response {
    let response = ui.interact(
        rect,
        ui.make_persistent_id(("color_icon", tooltip)),
        if tooltip == PLANNED_EYEDROPPER {
            egui::Sense::hover()
        } else {
            egui::Sense::click()
        },
    );
    ui.painter().text(
        rect.center(),
        egui::Align2::CENTER_CENTER,
        icon.as_str(),
        egui::FontId::new(12.0, egui::FontFamily::Name("phosphor-regular".into())),
        color,
    );
    response.on_hover_text(tooltip)
}

fn labeled_button(
    ui: &mut egui::Ui,
    rect: egui::Rect,
    icon: Icon,
    label: &str,
    accent: bool,
) -> egui::Response {
    let palette = crate::design_system::palette(ui);
    ui.push_id(("color_picker_footer_action", label), |ui| {
        ui.put(
            rect,
            egui::Button::new(crate::design_system::controls::icon_label(
                icon,
                label,
                TypeRole::Control,
                12.0,
                palette.text_primary,
            ))
            .fill(if accent {
                palette.accent
            } else {
                palette.control
            })
            .stroke(egui::Stroke::new(
                1.0_f32,
                if accent {
                    palette.accent
                } else {
                    palette.border
                },
            ))
            .corner_radius(4.0),
        )
    })
    .inner
}

fn field_background(ui: &egui::Ui, rect: egui::Rect) {
    let palette = crate::design_system::palette(ui);
    ui.painter().rect(
        rect,
        4.0,
        palette.control,
        egui::Stroke::new(1.0_f32, palette.border),
        egui::StrokeKind::Inside,
    );
}

fn caption(ui: &egui::Ui, root: egui::Rect, x: f32, y: f32, value: &str) {
    let palette = crate::design_system::palette(ui);
    text(
        ui,
        root.min + egui::vec2(x, y),
        egui::Align2::LEFT_TOP,
        value,
        TypeRole::Caption.font(),
        palette.text_muted,
    );
}

fn text(
    ui: &egui::Ui,
    position: egui::Pos2,
    align: egui::Align2,
    value: &str,
    font: egui::FontId,
    color: egui::Color32,
) {
    ui.painter().text(position, align, value, font, color);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn exported_popup_regions_match_penpot_geometry() {
        assert_eq!(POPUP_SIZE, egui::vec2(420.0, 350.0));
        assert_eq!(Geometry::HEADER_HEIGHT, 38.0);
        assert_eq!(Geometry::CONTENT.size(), egui::vec2(396.0, 202.0));
        assert_eq!(Geometry::WHEEL_EDITOR.size(), egui::vec2(178.0, 202.0));
        assert_eq!(Geometry::VALUES.size(), egui::vec2(198.0, 186.0));
        assert_eq!(Geometry::RECENTS.size(), egui::vec2(396.0, 28.0));
        assert_eq!(Geometry::FOOTER.size(), egui::vec2(396.0, 26.0));
    }

    #[test]
    fn exported_value_and_action_fields_retain_exact_sizes() {
        let root = egui::Rect::from_min_size(egui::Pos2::ZERO, POPUP_SIZE);
        assert_eq!(
            Geometry::at(root, 198.0, 144.0, 166.0, 24.0).size(),
            egui::vec2(166.0, 24.0)
        );
        assert_eq!(
            Geometry::at(root, 370.0, 144.0, 26.0, 24.0).size(),
            egui::vec2(26.0, 24.0)
        );
        assert_eq!(
            Geometry::at(root, 240.0, 256.0, 156.0, 14.0).size(),
            egui::vec2(156.0, 14.0)
        );
        assert_eq!(
            Geometry::at(root, 334.0, 316.0, 74.0, 26.0).size(),
            egui::vec2(74.0, 26.0)
        );
    }

    #[test]
    fn disabled_eyedropper_has_no_state_or_action_contract() {
        assert_eq!(PLANNED_EYEDROPPER, "Planned — screen sampling pending");
    }
}
