zilliz 1.4.3

TUI and CLI tool for managing Zilliz Cloud clusters and Milvus operations
Documentation
use ratatui::prelude::*;

use crate::tui::app::App;
use crate::tui::widgets::card::{self, Card};
use crate::tui::wizard::WizardFocus;

pub fn render(frame: &mut Frame, app: &App, area: Rect) {
    let wizard = match app.wizard.as_ref() {
        Some(w) => w,
        None => return,
    };

    let visible = wizard.api_key_visible;
    let buf = &wizard.api_key_buf;
    let displayed: String = if visible {
        buf.clone()
    } else {
        "".repeat(buf.chars().count())
    };
    let count_label = format!("{} chars", buf.chars().count());

    let input_focus = wizard.api_key_focus == WizardFocus::Input;
    let input_bg = if input_focus {
        Color::Rgb(30, 30, 50)
    } else {
        Color::Reset
    };

    // Build input row: "> {value}" with right-aligned count.
    let body: Vec<Line> = vec![
        Line::from(Span::styled(
            "api key",
            Style::default().fg(Color::DarkGray),
        )),
        Line::from(vec![
            Span::styled("> ", Style::default().fg(Color::Gray)),
            Span::styled(displayed, Style::default().fg(Color::White).bg(input_bg)),
            Span::styled(
                if input_focus { "" } else { " " },
                Style::default().fg(Color::White),
            ),
            Span::raw("   "),
            Span::styled(count_label, Style::default().fg(Color::DarkGray)),
        ]),
        Line::from(""),
        Line::from(vec![
            Span::styled("Find it at ", Style::default().fg(Color::DarkGray)),
            Span::styled(
                "cloud.zilliz.com → Settings → API Keys.",
                Style::default().fg(Color::Rgb(110, 180, 255)),
            ),
        ]),
        Line::from(""),
        radio_line(
            "Save credentials to ~/.zilliz/credentials",
            true,
            wizard.api_key_focus == WizardFocus::Save,
        ),
        Line::from(""),
        Line::from(Span::styled(
            "Warning: API keys grant full access to the resources their role allows.",
            Style::default().fg(Color::Rgb(255, 200, 80)),
        )),
        Line::from(Span::styled(
            "Prefer Auth0 for interactive use; reserve API keys for CI.",
            Style::default().fg(Color::Rgb(255, 200, 80)),
        )),
    ];

    card::render(
        frame,
        area,
        &Card {
            eyebrow: Some("SIGN IN · STEP 3 OF 3"),
            title: Some("Paste your API key."),
            body,
            footer: None,
            error: wizard.error.as_deref(),
            step: Some("3 / 3"),
            max_width: 80,
        },
    );
}

fn radio_line(label: &str, selected: bool, focused: bool) -> Line<'static> {
    let mark = if selected { "[x]" } else { "[ ]" };
    let label_style = if focused {
        Style::default()
            .fg(Color::White)
            .add_modifier(Modifier::BOLD)
    } else {
        Style::default().fg(Color::Gray)
    };
    Line::from(vec![
        Span::styled(
            mark.to_string(),
            Style::default().fg(if selected {
                Color::Green
            } else {
                Color::DarkGray
            }),
        ),
        Span::raw(" "),
        Span::styled(label.to_string(), label_style),
    ])
}