feat(dashboard): native host onboarding when disconnected
CI / Build & Test (push) Successful in 3m19s

- Add install-command helper (getInstallCommand + platform detection)
  with OS-specific one-liners and configurable base URL
- Show onboarding card on Overview when !hostConnected: copy button,
  platform switcher (macOS/Linux / Windows), and "Check again" button
- "Check again" triggers refresh and shows success/disconnected toast
- refresh() returns state so callers can react to hostConnected
- Add unit tests for install-commands (all platforms, base URL)
- Document "Native host not found" flow in README and INSTALLATION
This commit is contained in:
Raven Scott
2026-03-15 01:12:29 -04:00
parent 02094e8605
commit 410fd87357
7 changed files with 215 additions and 3 deletions
+67 -2
View File
@@ -2,8 +2,9 @@
* Overview page — summary stats, quick-action cards, and virtualized connection lists.
* Uses the OvList class for infinite-scroll, searchable tables of connections and servers.
* Depends on: core/utils.js ($, escapeHtml), ui/state-tag.js (stateTag),
* ui/modal.js (openModal), pages/ssh.js (openAddSshModal),
* pages/rdp.js (openAddRdpModal), core/state.js (sshConnections, rdpConnections)
* ui/modal.js (openModal), ui/toast.js (copyToClipboard, showToast),
* pages/ssh.js (openAddSshModal), pages/rdp.js (openAddRdpModal),
* core/state.js (sshConnections, rdpConnections), data/install-commands.js (getInstallCommand, detectPlatform, INSTALL_SCRIPT_BASE_URL)
*/
const OV_PAGE = 20; // rows per page
@@ -89,6 +90,22 @@ let _ovSvc = null;
let _ovSsh = null;
let _ovRdp = null;
/** Current platform for onboarding card (win32 | macos | linux). Set by detectPlatform or switcher. */
let _onboardingPlatform = null;
/**
* Update the onboarding card command and label for the given platform.
* @param {'macos'|'linux'|'win32'} platform
*/
function _updateOnboardingCommand(platform) {
if (typeof getInstallCommand === 'undefined' || typeof INSTALL_SCRIPT_BASE_URL === 'undefined') return;
const { label, command } = getInstallCommand(INSTALL_SCRIPT_BASE_URL, platform);
const labelEl = $('onboardingPlatformLabel');
const cmdEl = $('onboardingCommand');
if (labelEl) labelEl.textContent = label;
if (cmdEl) cmdEl.textContent = command;
}
/**
* Create and wire up the OvList instances for virtual hosts and server tunnels.
* Called once during dashboard initialisation.
@@ -188,6 +205,41 @@ function initOvLists() {
$('qaAddSsh') ?.addEventListener('click', () => openAddSshModal(null));
$('qaAddRdp') ?.addEventListener('click', () => openAddRdpModal(null));
$('qaInstallCA') ?.addEventListener('click', () => openModal('modal-installCA'));
// Onboarding card (native host not found): Copy, Check again, platform switcher
const copyBtn = $('onboardingCopyBtn');
if (copyBtn) {
copyBtn.addEventListener('click', () => {
const cmdEl = $('onboardingCommand');
if (cmdEl && typeof copyToClipboard === 'function') copyToClipboard(cmdEl.textContent, copyBtn);
});
}
const checkAgainBtn = $('onboardingCheckAgainBtn');
if (checkAgainBtn) {
checkAgainBtn.addEventListener('click', async () => {
if (typeof refresh !== 'function') return;
const state = await refresh();
if (state?.hostConnected) {
if (typeof showToast === 'function') showToast('Native host connected', 'success');
} else {
if (typeof showToast === 'function') showToast('Still disconnected. Install the native host using the command above.', 'default');
}
});
}
const switchMacLinux = $('onboardingSwitchMacLinux');
if (switchMacLinux) {
switchMacLinux.addEventListener('click', () => {
_onboardingPlatform = 'macos';
_updateOnboardingCommand('macos');
});
}
const switchWin = $('onboardingSwitchWin');
if (switchWin) {
switchWin.addEventListener('click', () => {
_onboardingPlatform = 'win32';
_updateOnboardingCommand('win32');
});
}
}
/**
@@ -251,6 +303,19 @@ function updateDashboard(state) {
const lu = $('overviewLastUpdated');
if (lu) lu.textContent = 'Updated ' + new Date().toLocaleTimeString();
// Onboarding card: show when native host is disconnected
const onboardingEl = $('onboardingNativeHost');
if (onboardingEl) {
if (!state.hostConnected) {
onboardingEl.style.display = 'block';
if (_onboardingPlatform == null && typeof detectPlatform === 'function') _onboardingPlatform = detectPlatform();
_updateOnboardingCommand(_onboardingPlatform || 'linux');
} else {
onboardingEl.style.display = 'none';
_onboardingPlatform = null;
}
}
setText('swarmCount', servers.length);
setText('connCount', virtualHosts.length);
setText('serviceTunnelCount', serviceTunnels.length);