Wider code display, copy button beside the value

Feedback on the add-node page: the command was hard to read and hard to copy.

Both the command and the six-digit code now use .otp-value-row / .otp-value - the
wide monospace value with its copy button beside it that the provisioning dialog
already uses. Reusing that rather than writing new CSS, because a second styling of
the same idea is how the two drift apart.

The command was a <textarea> carrying a class named `code-block` that exists nowhere
in the stylesheet - I had invented it - so it rendered as a default textarea rather
than the monospace block it was meant to be. It is now a <code>, which means it is
written with setText rather than .value; setting .value on a <code> silently does
nothing, which is the same class of silent failure as the missing toast element.

The standalone "Copy the command" and "Copy the code" buttons at the bottom are gone:
each value now has its own button next to it, so there is one obvious way to copy each
thing and no need to match a button to a field.

Done is now labelled to say what it does - "clear this from the page" - because the
code is only in the page's memory and the controller cannot produce it again.
This commit is contained in:
umbra audio
2026-10-03 12:03:39 -07:00
parent a75959c2e9
commit 62372a9c55
2 changed files with 41 additions and 22 deletions

View File

@@ -241,9 +241,10 @@
var commandField = query('#pairing-command');
if (commandField) {
// .value, never innerHTML: this is text a person will paste, and it contains a
// code. The server builds it so the console and a shell user get the same command.
commandField.value = result.command || '';
// textContent, never innerHTML: this is text a person will paste and it contains a
// code. The element is a <code>, so .value does not exist on it - setting it would
// silently do nothing, which is the same failure as the missing toast element.
setText(commandField, result.command || '');
}
var codeField = query('#pairing-code');
@@ -416,7 +417,7 @@
}
var commandField = query('#pairing-command');
if (commandField) {
commandField.value = '';
setText(commandField, '');
}
});
}
@@ -425,7 +426,7 @@
if (copyCommand) {
copyCommand.addEventListener('click', function () {
var field = query('#pairing-command');
copyText(field ? field.value : '', 'Command');
copyText(field ? field.textContent : '', 'Command');
});
}

View File

@@ -146,22 +146,40 @@
<h2>Run this on the machine</h2>
<p class="card-note" id="pairing-node-name-note"></p>
<div class="field">
<label for="pairing-command">Command</label>
<textarea id="pairing-command" class="code-block" rows="4" readonly
spellcheck="false"></textarea>
<small class="field-hint">
Read it before running it. It is a script fetched over the network, and the
command below is what fetches it.
</small>
<div class="otp-panel">
<div class="otp-panel-head">
<span class="field-label" id="pairing-command-label">Command</span>
</div>
<p class="otp-panel-target">
Paste this into a terminal on the machine you are onboarding. It downloads the
bootstrap; read it if you want to — it is a script fetched over the network.
</p>
<!-- The value and its copy button sit side by side, reusing the one-time-code
row the provisioning dialog already uses. A command a person is going to
retype should not be something they have to select by hand. -->
<div class="otp-value-row">
<code class="otp-value" id="pairing-command"></code>
<button class="button button--ghost" type="button" id="pairing-copy-command">
Copy
</button>
</div>
</div>
<div class="field">
<label for="pairing-code">Six-digit code</label>
<p class="pairing-code" id="pairing-code"></p>
<small class="field-hint" id="pairing-code-hint">
Shown once. It expires, and it is spent by the first machine that uses it.
</small>
<div class="otp-panel">
<div class="otp-panel-head">
<span class="field-label" id="pairing-code-label">Six-digit code</span>
</div>
<p class="otp-panel-target">
The bootstrap asks for this. It expires, and it is spent by the first machine
that uses it — so copy it before you log out of this page.
</p>
<div class="otp-value-row">
<code class="otp-value" id="pairing-code"></code>
<button class="button button--ghost" type="button" id="pairing-copy-code">
Copy
</button>
</div>
<p class="otp-hint" id="pairing-code-hint" role="status" aria-live="polite"></p>
</div>
<div class="alert alert--warn" id="pairing-warning" hidden>
@@ -172,9 +190,9 @@
</div>
<div class="form-actions">
<button class="button" type="button" id="pairing-copy-command">Copy the command</button>
<button class="button" type="button" id="pairing-copy-code">Copy the code</button>
<button class="button button--quiet" type="button" id="pairing-done">Done</button>
<button class="button button--quiet" type="button" id="pairing-done">
Done — clear this from the page
</button>
</div>
</div>
</div>