public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: Re: [PATCH datacenter-manager v2] ui: auto-installer: show assistant command line in text area
Date: Wed, 11 Nov 2026 10:54:39 +0100	[thread overview]
Message-ID: <4ab4eaba-6036-4802-bb26-3754b1da4bff@proxmox.com> (raw)
In-Reply-To: <20260605142631.1576216-1-c.heiss@proxmox.com>

Ping, still applies.

On 6/5/26 16:26, Christoph Heiss wrote:
> .. instead of a single-line, password-type input.
> 
> Adds a simple component for displaying the command line as a
> `<textarea>` element, hiding the contents by default.
> 
> The show/hide icon was converted into a button and moved outside the
> input element itself - as having it inside the textarea is kind of
> awkward.
> 
> Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
> ---
> v1: https://lore.proxmox.com/pdm-devel/20260528111123.1225933-1-c.heiss@proxmox.com/
> 
> Results in a slightly different UI for the token field vs. command line
> field, but definitely makes sense having a textarea there IMHO.
> 
> The idea is based on an idea from the v1's discussion, to have a
> separate component for it. Displaying the command line in a textarea
> (which by design is made for displaying lots of text) came to mind,
> instead of a single-line input element, hiding most of the command line.
> 
> To component could be factored out in the future if ever needed, but
> as said in the previous thread, it's bit of a special case.
> 
>  .../command_line_display_field.rs             | 118 ++++++++++++++++++
>  ui/src/remotes/auto_installer/mod.rs          |   1 +
>  .../auto_installer/prepared_answer_form.rs    |  55 ++++----
>  3 files changed, 140 insertions(+), 34 deletions(-)
>  create mode 100644 ui/src/remotes/auto_installer/command_line_display_field.rs
> 
> diff --git a/ui/src/remotes/auto_installer/command_line_display_field.rs b/ui/src/remotes/auto_installer/command_line_display_field.rs
> new file mode 100644
> index 00000000..76c601c7
> --- /dev/null
> +++ b/ui/src/remotes/auto_installer/command_line_display_field.rs
> @@ -0,0 +1,118 @@
> +//! A hidden-by-default [`TextArea`] to display the generated command line for
> +//! `proxmox-auto-install-assistant`.
> +
> +use yew::Properties;
> +
> +use proxmox_yew_comp::utils::copy_text_to_clipboard;
> +use pwt::{
> +    css::{self, ColorScheme},
> +    prelude::*,
> +    widget::{Button, Column, Row, Tooltip, form::TextArea},
> +};
> +use pwt_macros::{builder, widget};
> +
> +#[widget(comp=CommandLineDisplayFieldComponent, @input)]
> +#[derive(Clone, PartialEq, Properties)]
> +#[builder]
> +pub struct CommandLineDisplayField {
> +    /// Content to display.
> +    #[prop_or_default]
> +    #[builder]
> +    value: AttrValue,
> +}
> +
> +impl CommandLineDisplayField {
> +    pub fn new() -> Self {
> +        yew::props!(Self {})
> +    }
> +}
> +
> +enum Message {
> +    Reveal,
> +    Hide,
> +}
> +
> +struct CommandLineDisplayFieldComponent {
> +    hidden: bool,
> +}
> +
> +impl Component for CommandLineDisplayFieldComponent {
> +    type Properties = CommandLineDisplayField;
> +    type Message = Message;
> +
> +    fn create(_ctx: &Context<Self>) -> Self {
> +        Self { hidden: true }
> +    }
> +
> +    fn view(&self, ctx: &Context<Self>) -> Html {
> +        let props = ctx.props();
> +        let hidden = self.hidden;
> +
> +        let toggle_hidden_cb = ctx.link().callback(move |_| {
> +            if hidden {
> +                Self::Message::Reveal
> +            } else {
> +                Self::Message::Hide
> +            }
> +        });
> +
> +        let (toggle_hidden_icon, toggle_hidden_tip, value) = if hidden {
> +            (
> +                "fa fa-eye",
> +                tr!("Show Command Line"),
> +                "\u{2022}".repeat(props.value.len()).into(),
> +            )
> +        } else {
> +            (
> +                "fa fa-eye-slash",
> +                tr!("Hide Command Line"),
> +                props.value.clone(),
> +            )
> +        };
> +
> +        Row::new()
> +            .with_std_props(&props.std_props)
> +            .class(css::Display::Flex)
> +            .class("pwt-fill-grid-row")
> +            .gap(2)
> +            .with_child(
> +                TextArea::new()
> +                    .attribute("rows", "10")
> +                    .attribute("resize", "both")
> +                    .read_only(true)
> +                    .class("pwt-w-100")
> +                    .value(value)
> +            )
> +            .with_child(
> +                    Column::new().gap(2).with_child(
> +                    Tooltip::new(
> +                            Button::new_icon(toggle_hidden_icon)
> +                                .class(ColorScheme::Primary)
> +                                .on_activate(toggle_hidden_cb)
> +                    )
> +                    .tip(toggle_hidden_tip),
> +                )
> +                .with_child(
> +                        Tooltip::new(
> +                            Button::new_icon("fa fa-clipboard")
> +                                .class(ColorScheme::Primary)
> +                                .on_activate({
> +                                    let value = props.value.clone();
> +                                    move |_| copy_text_to_clipboard(&value)
> +                                }),
> +                        )
> +                        .tip(tr!("Copy template command line to clipboard. Replace INPUT.iso with your installation ISO.")),
> +                )
> +            )
> +            .into()
> +    }
> +
> +    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
> +        match msg {
> +            Self::Message::Reveal => self.hidden = false,
> +            Self::Message::Hide => self.hidden = true,
> +        }
> +
> +        true
> +    }
> +}
> diff --git a/ui/src/remotes/auto_installer/mod.rs b/ui/src/remotes/auto_installer/mod.rs
> index 99508f4b..1e5fb437 100644
> --- a/ui/src/remotes/auto_installer/mod.rs
> +++ b/ui/src/remotes/auto_installer/mod.rs
> @@ -1,5 +1,6 @@
>  //! Implements the UI for the proxmox-auto-installer integration.
>  
> +mod command_line_display_field;
>  mod installations_panel;
>  mod prepared_answer_add_wizard;
>  mod prepared_answer_edit_window;
> diff --git a/ui/src/remotes/auto_installer/prepared_answer_form.rs b/ui/src/remotes/auto_installer/prepared_answer_form.rs
> index 960decae..db2701a2 100644
> --- a/ui/src/remotes/auto_installer/prepared_answer_form.rs
> +++ b/ui/src/remotes/auto_installer/prepared_answer_form.rs
> @@ -32,7 +32,7 @@ use pwt::{
>      props::FieldStdProps,
>      state::Store,
>      widget::{
> -        Button, Column, Container, Dialog, Fa, FieldLabel, FieldPosition, InputPanel, Row, Tooltip,
> +        Button, Container, Dialog, Fa, FieldLabel, FieldPosition, InputPanel, Row, Tooltip,
>          form::{
>              Checkbox, Combobox, DisplayField, Field, FormContext, InputType, Number, TextArea,
>              ValidateFn,
> @@ -41,7 +41,9 @@ use pwt::{
>  };
>  
>  use super::pdm_origin;
> -use crate::remotes::auto_installer::token_selector::TokenSelector;
> +use crate::remotes::auto_installer::{
> +    command_line_display_field::CommandLineDisplayField, token_selector::TokenSelector,
> +};
>  
>  pub fn prepare_form_data(mut value: serde_json::Value) -> Result<serde_json::Value> {
>      let obj = value
> @@ -1010,7 +1012,7 @@ pub fn render_show_secret_dialog(
>  
>      let copy_token_view = Container::new()
>          .key("copy-token-view")
> -        .class("pwt-form-grid-col4")
> +        .class("pwt-form-grid-col4 pwt-w-100")
>          .with_child(FieldLabel::new(tr!("Token")))
>          .with_child(
>              Row::new()
> @@ -1043,45 +1045,27 @@ pub fn render_show_secret_dialog(
>              .unwrap_or_else(|| "https://pdm.example.com:8443")
>      );
>  
> -    let mut commandline = format!(
> -        "proxmox-auto-install-assistant prepare-iso --fetch-from http --url {answer_url} --answer-auth-token {token}",
> -    );
> +    let mut commandline = vec![
> +        "proxmox-auto-install-assistant prepare-iso".into(),
> +        "--fetch-from http".into(),
> +        format!("--url {answer_url}"),
> +        format!("--answer-auth-token {token}"),
> +    ];
>  
>      if let Some(fingerprint) = fingerprint {
> -        commandline = format!("{commandline} --cert-fingerprint {fingerprint}");
> +        commandline.push(format!("--cert-fingerprint {fingerprint}"));
>      }
>  
> -    commandline = format!("{commandline} INPUT.iso");
> +    commandline.push("INPUT.iso".into());
> +    let commandline = commandline.join(" \\\n  ");
>  
>      let copy_commandline_view = Container::new()
>          .key("copy-commandline-view")
> -        .class("pwt-form-grid-col4")
> +        .class("pwt-form-grid-col4 pwt-w-100")
>          .with_child(FieldLabel::new(tr!("Command Line")))
> -        .with_child(
> -            Row::new()
> -                .class("pwt-fill-grid-row")
> -                .gap(2)
> -                .with_child(
> -                    Field::new()
> -                        .input_type(InputType::Password)
> -                        .class(FlexFit)
> -                        .value(commandline.to_owned())
> -                        .read_only(true),
> -                )
> -                .with_child(
> -                    Tooltip::new(
> -                        Button::new_icon("fa fa-clipboard")
> -                            .class(ColorScheme::Primary)
> -                            .on_activate({
> -                                let commandline = commandline.to_owned();
> -                                move |_| copy_text_to_clipboard(&commandline)
> -                            }),
> -                    )
> -                    .tip(tr!("Copy template command line to clipboard. Replace INPUT.iso with your installation ISO.")),
> -                ),
> -        );
> +        .with_child(CommandLineDisplayField::new().value(commandline.clone().into()));
>  
> -    let mut panel = InputPanel::new().padding(4);
> +    let mut panel = InputPanel::new().padding(4).class("pwt-w-100");
>  
>      if let Some(id) = config_id {
>          panel.add_large_field(
> @@ -1095,7 +1079,10 @@ pub fn render_show_secret_dialog(
>      panel.add_large_custom_child(copy_token_view);
>      panel.add_large_custom_child(copy_commandline_view);
>  
> -    let dialog = Dialog::new(tr!("New Answer Token")).on_close(on_close).with_child(Column::new().with_child(panel))
> +    let dialog = Dialog::new(tr!("New Answer Token"))
> +        .width(900)
> +        .on_close(on_close)
> +        .with_child(Container::new().class(FlexFit).class("pwt-w-100").with_child(panel))
>          .with_child(
>              Container::new()
>                  .padding(4)





      reply	other threads:[~2026-11-11  9:54 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-05 14:25 [PATCH datacenter-manager v2] ui: auto-installer: show assistant command line in text area Christoph Heiss
2026-11-11  9:54 ` Christoph Heiss [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4ab4eaba-6036-4802-bb26-3754b1da4bff@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal