+
+
@@ -1660,6 +1665,8 @@ function vvMsHostChanged() {
const host = _vvMsHosts.find(h => h.slot === slot);
const stat = document.getElementById('vv-ms-host-status');
+ vvMsUpdatePreview(); // the destination just changed, so the command shown must too
+
if (!host) { stat.textContent = ''; return; }
const onlineTxt = host.online === null ? 'status unknown'
@@ -1720,6 +1727,14 @@ function vvMsBrowse() {
});
}
+// The whole command, with the real paths in it, not "…src host:dst".
+//
+// Two reasons it has to resolve rather than gesture. The first is the trailing slash: this card
+// already warns that "/" means contents and no "/" means the folder, and a hint under a text box
+// is not where anyone looks at the moment they press Run — in the command it is unmissable. The
+// second is --delete, which is one checkbox among nine and is the only one that removes data at
+// the far end. Seeing it sit next to the actual destination is the difference between reading a
+// flag and understanding a consequence.
function vvMsUpdatePreview() {
const flags = Array.from(document.querySelectorAll('#vv-ms-card input[data-flag]:checked'))
.map(el => el.dataset.flag).join(' ');
@@ -1728,6 +1743,31 @@ function vvMsUpdatePreview() {
const bwPrev= document.getElementById('vv-ms-bwlimit-preview');
if (prev) prev.textContent = flags || '(none)';
if (bwPrev) bwPrev.textContent = bw > 0 ? '--bwlimit=' + bw : '';
+
+ const local = document.getElementById('vv-ms-local')?.value.trim() || '';
+ const rpath = document.getElementById('vv-ms-rpath')?.value.trim() || '';
+ const user = document.getElementById('vv-ms-user')?.value || 'root';
+ const hostEl = document.getElementById('vv-ms-host');
+ const hostTxt = hostEl && hostEl.selectedIndex >= 0
+ ? (hostEl.options[hostEl.selectedIndex].textContent || '').trim() : '';
+ const tgt = document.getElementById('vv-ms-target-preview');
+ if (tgt) {
+ tgt.textContent = (local || '') + ' ' + user + '@' + (hostTxt || '') + ':' + (rpath || '');
+ }
+
+ // Destructive styling follows the checkbox, so the warning cannot be left on screen after the
+ // flag is cleared — a stale danger marker is how a real one stops being read.
+ const del = document.querySelector('#vv-ms-card input[data-flag="--delete"]');
+ const dry = document.getElementById('vv-ms-dryrun');
+ const warn = document.getElementById('vv-ms-delete-warn');
+ if (warn) {
+ const armed = del && del.checked && !(dry && dry.checked);
+ warn.style.display = armed ? '' : 'none';
+ if (armed) {
+ warn.textContent = 'deletes anything at ' + (rpath || 'the destination')
+ + ' that is not in ' + (local || 'the source');
+ }
+ }
}
function _vvMsRenderDirs(dirsElId, dirs, parent, navFn) {
@@ -1815,7 +1855,7 @@ function vvMsBrowseLocal() {
});
}
-function vvMsRun() {
+async function vvMsRun() {
const local = document.getElementById('vv-ms-local').value.trim();
const slot = document.getElementById('vv-ms-host').value;
const rpath = document.getElementById('vv-ms-rpath').value.trim();
@@ -1831,6 +1871,24 @@ function vvMsRun() {
if (!slot) { vvAlert('Select a remote server.'); return; }
if (!rpath) { vvAlert('Enter a remote destination path.'); return; }
+ // The only confirm on this card, and only for the one flag that destroys data at the far end.
+ // Guarding every run would train the reflex that dismisses this one — a prompt that appears on
+ // every safe action is not read by the time an unsafe one arrives.
+ //
+ // Skipped under --dry-run, because that is exactly the rehearsal this is asking for, and
+ // refusing to let someone rehearse the dangerous case cheaply is the wrong lesson to teach.
+ if (flags.includes('--delete') && !isDry) {
+ const ok = await vvConfirm(
+ 'This deletes anything in ' + rpath + ' on ' + slot + ' that is not in ' + local + '.\n\n'
+ + (local.endsWith('/')
+ ? 'The source ends in "/", so its CONTENTS are compared against the destination.'
+ : 'The source has no trailing "/", so the FOLDER ITSELF is copied into the destination — '
+ + 'a common cause of deleting the wrong level.')
+ + '\n\nRun a --dry-run first if you have not.',
+ { title: 'Run with --delete?', confirmText: 'Run it', type: 'warning' });
+ if (!ok) return;
+ }
+
const btn = document.getElementById('vv-ms-run-btn');
const stat = document.getElementById('vv-ms-run-status');
const out = document.getElementById('vv-ms-out');