From: Christoph Heiss <c.heiss@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [PATCH datacenter-manager v2] ui: auto-installer: show assistant command line in text area
Date: Fri, 5 Jun 2026 16:25:19 +0200 [thread overview]
Message-ID: <20260605142631.1576216-1-c.heiss@proxmox.com> (raw)
.. 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)
--
2.54.0
reply other threads:[~2026-06-05 14:27 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=20260605142631.1576216-1-c.heiss@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