(function () { /** * Nexus Terminal — settings modal. * Renders a tabbed settings dialog for application-wide preferences, * terminal defaults, security, known-hosts management, and about info. */ const { createElement, escapeHtml, formatTimestamp, $ } = window.NexusTerminal; /** Settings tab definitions. */ const TABS = [ { key: "general", label: "General" }, { key: "terminal", label: "Terminal" }, { key: "security", label: "Security" }, { key: "knownhosts", label: "Known Hosts" }, { key: "help", label: "Help" }, { key: "about", label: "About" }, ]; const KONAMI_SEQUENCE = [ "ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a", ]; class Settings { /** * @param {App} app */ constructor(app) { this.app = app; /** @type {HTMLElement|null} */ this._overlay = null; /** @type {string} Currently active tab key. */ this._activeTab = "general"; /** @type {Object} Working copy of preferences. */ this._prefs = {}; /** @type {Array} Cached known hosts list. */ this._knownHosts = []; this._aboutKonamiHandler = null; this._konamiIndex = 0; this._konamiTogglePending = false; } // ── Public API ─────────────────────────────────────────────────── /** * Opens the settings modal. */ async open(tabKey) { this._prefs = JSON.parse(JSON.stringify(this.app.state.preferences)); this._activeTab = tabKey || this._activeTab || "general"; try { this._knownHosts = (await window.NexusTerminal.wailsService.ListKnownHosts()) || []; } catch (_) { this._knownHosts = []; } try { this._defaultShell = await window.NexusTerminal.wailsService.GetDefaultShell(); } catch (_) { this._defaultShell = ""; } this._render(); } /** * Opens the settings modal on a specific tab. * @param {string} tabKey */ async openTab(tabKey) { this._activeTab = tabKey || "general"; return this.open(this._activeTab); } /** * Closes the settings modal. */ close() { this._detachAboutKonamiListener(); if (this._overlay) { this._overlay.remove(); this._overlay = null; } } /** * Saves the modified preferences to the backend. */ async save() { this._collectValues(); try { await window.NexusTerminal.wailsService.SavePreferences(this._prefs); this.app.state.setPreferences(this._prefs); // Apply terminal colour theme change immediately. if (this.app.terminals && this._prefs.terminalTheme) { this.app.terminals.applyTheme(this._prefs.terminalTheme); } if (this.app && typeof this.app._applyPreferences === "function") { this.app._applyPreferences(); } this.app.notifications.success("Settings saved"); this.close(); } catch (err) { this.app.notifications.error("Failed to save settings: " + err); } } // ── Rendering ──────────────────────────────────────────────────── /** @private */ _render() { this.close(); const overlay = createElement("div", "modal-backdrop"); const modal = createElement("div", "modal modal-settings"); // Header const header = createElement("div", "modal-header"); header.innerHTML = ` `; header .querySelector(".modal-close") .addEventListener("click", () => this.close()); modal.appendChild(header); // Tab bar const tabBar = createElement("div", "settings-tab-bar"); for (const tab of TABS) { const btn = createElement( "button", `settings-tab ${tab.key === this._activeTab ? "active" : ""}`, { textContent: tab.label, dataset: { tab: tab.key }, }, ); btn.addEventListener("click", () => { this._activeTab = tab.key; this._renderBody(modal); tabBar.querySelectorAll(".settings-tab").forEach((t) => { t.classList.toggle("active", t.dataset.tab === tab.key); }); }); tabBar.appendChild(btn); } modal.appendChild(tabBar); // Body (tab content) const body = createElement("div", "modal-body settings-body"); modal.appendChild(body); this._renderTabContent(body); // Footer const footer = createElement("div", "modal-footer"); const cancelBtn = createElement("button", "btn btn-secondary", { textContent: "Cancel", }); const saveBtn = createElement("button", "btn btn-primary", { textContent: "Save", }); cancelBtn.addEventListener("click", () => this.close()); saveBtn.addEventListener("click", () => this.save()); footer.appendChild(cancelBtn); footer.appendChild(saveBtn); modal.appendChild(footer); overlay.appendChild(modal); overlay.addEventListener("click", (e) => { if (e.target === overlay) this.close(); }); modal.addEventListener("click", (e) => e.stopPropagation()); document.body.appendChild(overlay); this._overlay = overlay; // Trigger visibility after append so CSS transitions animate requestAnimationFrame(() => { overlay.classList.add("is-visible"); modal.classList.add("is-visible"); }); } /** * Re-renders only the body when the active tab changes. * @param {HTMLElement} modal * @private */ _renderBody(modal) { const body = modal.querySelector(".settings-body"); if (body) this._renderTabContent(body); } /** * Renders the content for the currently active tab. * @param {HTMLElement} body * @private */ _renderTabContent(body) { this._detachAboutKonamiListener(); body.innerHTML = ""; body.classList.remove("help-layout"); switch (this._activeTab) { case "general": this._renderGeneralTab(body); break; case "terminal": this._renderTerminalTab(body); break; case "security": this._renderSecurityTab(body); break; case "knownhosts": this._renderKnownHostsTab(body); break; case "help": this._renderHelpTab(body); break; case "about": this._renderAboutTab(body); break; } } // ── Tab content renderers ──────────────────────────────────────── /** * General preferences tab. * @param {HTMLElement} body * @private */ _renderGeneralTab(body) { const p = this._prefs; body.appendChild( this._settingField("select", "theme", "Theme", p.theme, { options: [ { value: "dark", label: "Dark" }, { value: "light", label: "Light" }, ], }), ); body.appendChild( this._settingField( "select", "defaultProtocol", "Default Protocol", p.defaultProtocol, { options: [ { value: "ssh", label: "SSH" }, { value: "telnet", label: "Telnet" }, { value: "raw", label: "RAW" }, ], }, ), ); body.appendChild( this._settingField( "toggle", "confirmOnClose", "Confirm on close", p.confirmOnClose, ), ); body.appendChild( this._settingField( "toggle", "confirmOnDisconnect", "Confirm on disconnect", p.confirmOnDisconnect, ), ); body.appendChild( this._settingField( "toggle", "showStatusBar", "Show status bar", p.showStatusBar, ), ); body.appendChild( this._settingField( "number", "sidebarWidth", "Sidebar width (px)", p.sidebarWidth, { min: 180, max: 500 }, ), ); // Local shell path with browse button const shellGroup = createElement("div", "form-group settings-field"); const shellLabel = createElement("label", "form-label", { textContent: "Local shell", for: "setting-localShell", }); shellGroup.appendChild(shellLabel); const shellRow = createElement("div", "settings-shell-row"); const shellInput = createElement("input", "form-input", { type: "text", id: "setting-localShell", name: "setting-localShell", value: p.localShell || "", placeholder: this._defaultShell || "/bin/zsh", }); shellRow.appendChild(shellInput); const browseBtn = createElement("button", "btn btn-sm btn-secondary", { textContent: "Browse…", type: "button", }); browseBtn.addEventListener("click", async () => { try { const path = await window.NexusTerminal.wailsService.BrowseForShell(); if (path) { shellInput.value = path; } } catch (_) {} }); shellRow.appendChild(browseBtn); shellGroup.appendChild(shellRow); const shellHint = createElement("span", "form-hint", { textContent: "Leave empty to use system default", }); shellGroup.appendChild(shellHint); body.appendChild(shellGroup); } /** * Terminal defaults tab. * @param {HTMLElement} body * @private */ _renderTerminalTab(body) { const p = this._prefs; // Terminal colour theme selector const themes = window.NexusTerminal.listTerminalThemes(); body.appendChild( this._settingField( "select", "terminalTheme", "Colour scheme", p.terminalTheme || "charcoal", { options: themes, }, ), ); body.appendChild( this._settingField( "text", "defaultFontFamily", "Font family", p.defaultFontFamily, { placeholder: "JetBrains Mono, Menlo, Monaco, Consolas, monospace", }, ), ); body.appendChild( this._settingField( "number", "defaultFontSize", "Font size", p.defaultFontSize, { min: 8, max: 36 }, ), ); body.appendChild( this._settingField( "number", "defaultScrollback", "Scrollback lines", p.defaultScrollback, { min: 0, max: 100000 }, ), ); body.appendChild( this._settingField( "select", "defaultCursorStyle", "Cursor style", p.defaultCursorStyle, { options: [ { value: "block", label: "Block" }, { value: "underline", label: "Underline" }, { value: "bar", label: "Bar" }, ], }, ), ); body.appendChild( this._settingField( "toggle", "useNativePaste", "Use native paste", p.useNativePaste === true, ), ); } /** * Security settings tab. * @param {HTMLElement} body * @private */ _renderSecurityTab(body) { const storeState = this.app.state.secretStoreUnlocked ? "Unlocked" : "Locked"; const storeInit = this.app.state.secretStoreInitialised; const statusRow = createElement("div", "settings-row"); statusRow.innerHTML = ` Secret store ${storeState} `; body.appendChild(statusRow); if (!storeInit) { const initBtn = createElement("button", "btn btn-primary btn-sm", { textContent: "Initialise Secret Store", }); initBtn.addEventListener("click", async () => { try { const pw = await this.app.secretPrompt.promptMasterPassword(true); await window.NexusTerminal.wailsService.InitialiseSecretStore(pw); this.app.state.setSecretStoreInitialised(true); this.app.state.setSecretStoreState(true); this.app.notifications.success("Secret store created"); this._renderTabContent(body); } catch (err) { if (err.message !== "User cancelled") { this.app.notifications.error("Failed to initialise: " + err); } } }); body.appendChild(initBtn); } else if (!this.app.state.secretStoreUnlocked) { const unlockBtn = createElement("button", "btn btn-primary btn-sm", { textContent: "Unlock", }); unlockBtn.addEventListener("click", async () => { try { const pw = await this.app.secretPrompt.promptMasterPassword(false); await window.NexusTerminal.wailsService.UnlockSecretStore(pw); this.app.state.setSecretStoreState(true); this.app.notifications.success("Secret store unlocked"); this._renderTabContent(body); } catch (err) { if (err.message !== "User cancelled") { this.app.notifications.error("Unlock failed: " + err); } } }); body.appendChild(unlockBtn); } else { const lockBtn = createElement("button", "btn btn-outline btn-sm", { textContent: "Lock", }); lockBtn.addEventListener("click", async () => { try { await window.NexusTerminal.wailsService.LockSecretStore(); this.app.state.setSecretStoreState(false); this.app.notifications.info("Secret store locked"); this._renderTabContent(body); } catch (err) { this.app.notifications.error("Lock failed: " + err); } }); body.appendChild(lockBtn); } } /** * Known-hosts management tab. * @param {HTMLElement} body * @private */ _renderKnownHostsTab(body) { if (this._knownHosts.length === 0) { body.appendChild( createElement("p", "text-muted", { textContent: "No known hosts recorded.", }), ); return; } const table = createElement("table", "settings-table"); table.innerHTML = ` Host Port Key Type Fingerprint First Seen `; const tbody = createElement("tbody"); for (const kh of this._knownHosts) { const tr = createElement("tr"); tr.innerHTML = ` ${escapeHtml(kh.host)} ${kh.port} ${escapeHtml(kh.keyType)} ${escapeHtml(kh.fingerprint.slice(0, 20))}… ${formatTimestamp(kh.firstSeen)} `; const deleteBtn = createElement("button", "btn btn-xs btn-danger", { textContent: "Remove", }); deleteBtn.addEventListener("click", async () => { try { await window.NexusTerminal.wailsService.DeleteKnownHost(kh.id); this._knownHosts = this._knownHosts.filter((h) => h.id !== kh.id); this._renderTabContent(body); this.app.notifications.success("Known host removed"); } catch (err) { this.app.notifications.error("Failed to remove: " + err); } }); tr.querySelector("td:last-child").appendChild(deleteBtn); tbody.appendChild(tr); } table.appendChild(tbody); body.appendChild(table); } /** * In-app help and documentation tab. * @param {HTMLElement} body * @private */ _renderHelpTab(body) { const HELP_SECTIONS = [ { id: "getting-started", title: "Getting Started", content: `

Welcome to Nexus Terminal — a desktop application for SSH, Telnet, RAW TCP, VNC, GitHub, local terminal, and Leviathan binary-analysis workflows.

Creating Your First Connection

  1. Click the + button at the top of the sidebar, or press ⌘N (Mac) / Ctrl+N (Windows/Linux).
  2. Choose a protocol (SSH, Telnet, RAW, VNC, or GitHub).
  3. Fill in the hostname, port, and any credentials required.
  4. Optionally assign the connection to a group to keep your sidebar organised.
  5. Click Save, then double-click the entry in the sidebar to connect.

Leviathan Tools

Click Leviathan beside Nexus Terminal in the sidebar header to switch from connection profiles to binary-analysis tools. Use Load to choose a local file, then open tools such as Hex Editor, Overview, Entropy, Carve, BinSnoop, Structures, and Spectrogram View.

Quick Connect

Need to connect quickly without saving a profile? Press ⌘⇧Q / Ctrl+Shift+Q to open the quick connect dialog. Choose SSH, Telnet, or RAW, enter the host and port, and you're connected immediately.

Managing Connections

Right-click any connection in the sidebar to edit, duplicate, or delete it. You can drag connections between groups, and use the search box to filter by name, host, or tag.

`, }, { id: "ssh", title: "SSH Connections", content: `

SSH (Secure Shell) is the most common way to connect to remote servers. Nexus Terminal supports:

TOTP / 2FA Auto-Fill

If your server requires a time-based one-time password (TOTP) during login, you can store the TOTP secret in the profile editor under the TOTP / 2FA section. When connecting, Nexus Terminal will detect 2FA prompts (such as "Verification code" or "OTP") and automatically generate and submit a 6-digit code. Use the Test button to verify your secret generates valid codes before connecting.

Jump Hosts (Bastions)

If you need to reach a server through an intermediate host, add one or more jump hosts in the connection editor. Nexus Terminal will tunnel through each hop automatically.

Port Forwarding

You can set up local, remote, and dynamic (SOCKS proxy) port forwards in the connection editor. These start automatically when the session connects.

Proxy Support

If you need to connect through a proxy, open the Proxy section in the profile editor. Nexus Terminal supports SOCKS5 and HTTP CONNECT proxies, with optional username/password authentication. The proxy is used to reach the SSH server (or the first jump host if jump hosts are configured).

SFTP File Browser

Once connected to an SSH server, click the 📁 icon on the tab to open the SFTP file browser. You can browse directories, upload, download, rename, and delete files from a side panel.

SSH Agent Forwarding

Tick "Agent Forwarding" in the SSH settings to forward your local SSH agent to the remote server. This lets you use your local keys when connecting onward from that server.

Auto-Login Scripts

Define expect-style login scripts to automate interactions after connecting. In the Login Script section of the profile editor, add a sequence of Wait for (match text from the server) and Send (send text + newline) steps. For example:

  1. Wait for: login:
  2. Send: admin
  3. Wait for: Password:
  4. Send: mysecretpassword

Each wait step has a configurable timeout (default 10 seconds). Steps execute in order after the connection is established.

Auto-Reconnect

Enable Auto-Reconnect in the Advanced section to automatically reconnect when the connection drops unexpectedly. Reconnection uses exponential backoff (1s, 2s, 4s, 8s... up to 30s). You can configure the maximum number of attempts (default 5).

`, }, { id: "telnet", title: "Telnet Connections", content: `

Telnet is an older, unencrypted protocol still used for network equipment, legacy systems, and some fun internet services.

Note: Telnet sends everything in plain text. Avoid using it for anything sensitive over an untrusted network.

`, }, { id: "raw", title: "RAW TCP Connections", content: `

RAW TCP lets you connect to any TCP port and exchange data directly — useful for debugging network services, testing APIs, or working with custom protocols.

`, }, { id: "vnc", title: "VNC Connections", content: `

VNC profiles open a remote desktop viewer inside the Nexus workspace.

Prefer SSH tunnel for VNC outside trusted networks. Direct VNC is supported for isolated or trusted networks, but many classic VNC deployments do not provide strong transport encryption.

`, }, { id: "github", title: "GitHub Connections", content: `

The GitHub connection type lets you browse your repositories without leaving Nexus Terminal.

Setting Up

  1. Create a new connection and choose GitHub as the protocol.
  2. Enter your Personal Access Token (create one at GitHub → Settings → Developer settings → Personal access tokens). The token needs the repo scope.
  3. Save and connect.

What You'll See

Your repositories are listed grouped by organisation or owner, sorted by last commit date (newest first). The list refreshes automatically every few minutes.

`, }, { id: "leviathan", title: "Leviathan Binary Tools", content: `

Leviathan is the built-in binary-analysis and editing workbench. It is useful for firmware images, unknown binaries, archives, captures, embedded files, documents, and audio spectrogram experiments.

Opening and Saving Files

Switch to Leviathan in the sidebar header, click Load, and select a local file. The file becomes the active working document. Use Save to write the patched working copy to a destination you choose.

Leviathan tools open in normal tabs beside terminal and VNC sessions. They close immediately without a disconnect confirmation.

Hex Editor

Triage Tools

Overview, Hashes, Entropy, Strings, IOCs, Search, and Multi-search provide a first pass over the current document. The Entropy tool renders the whole-file graph and lets you open selected ranges in the Hex Editor.

Structure and Recovery Tools

Structures, Disasm, PDF, SquashFS, Carve, Keys, Timestamps, Stego, Metadata, BinSnoop, and Rules help identify embedded formats, files, signatures, strings, keys, and suspicious structures. Carve and BinSnoop can export discovered files or ranges.

Edit, Transform, and Case Tools

Patch Plan validates patch scripts, Patch Apply applies them, Transforms runs hex/Base64/URL/XOR/inflate/cipher pipelines, and Case Export packages investigation artefacts.

Spectrogram Tools

Spectrogram Encode turns an image into WAV audio or inserts an encoded image into an existing WAV/MP3 carrier. Spectrogram View visualises WAV or MP3 audio as a spectrogram image with controls for FFT size, height, aspect ratio, frequency range, brightness, and contrast.

Large Files

Leviathan keeps loaded files file-backed where possible and saves patched working copies in chunks. Tools that require complete image, audio, PDF, or other parser buffers enforce explicit size limits and show a clear message if an input is too large.

`, }, { id: "sessions", title: "Sessions & Tabs", content: `

Tab Management

Split Panes

You can split the terminal into multiple panes:

Drag the divider between panes to resize them.

Broadcast Input

Press ⌘⇧B / Ctrl+Shift+B to toggle broadcast mode. When active, everything you type is sent to all connected sessions simultaneously — perfect for running the same command across multiple servers. The broadcast button in the tab bar pulses red to remind you it's active.

Session Recording

Press ⌘⇧R / Ctrl+Shift+R to start recording a session. A red dot will appear on the tab. Press the same shortcut again to stop. Browse and replay recordings from the ⏺ button in the sidebar.

Right-Click Context Menu

Right-click in the terminal to access a context menu with:

`, }, { id: "power", title: "Power Features", content: `

Snippet Library

Press ⌘⇧S / Ctrl+Shift+S to open the snippet library. Save frequently-used commands with a name, description, and optional folder and tags. Click Send to Session to inject a command into the active terminal. You can also access snippets from the right-click context menu.

Terminal Search

Press ⌘F / Ctrl+F to open the search overlay on the active terminal. Features include:

Quick Connect

Press ⌘⇧Q / Ctrl+Shift+Q to open a lightweight connect dialog. Choose SSH, Telnet, or RAW; enter the host and port; and connect immediately without creating a saved profile.

Auto-Login Scripts

For SSH and Telnet connections, you can define expect-style login scripts — a sequence of "wait for text" and "send text" steps that execute automatically after connecting. Configure them in the Login Script section of the profile editor.

Auto-Reconnect

Enable auto-reconnect in a profile's settings to automatically reconnect when a connection drops. Uses exponential backoff with a configurable maximum number of attempts.

`, }, { id: "security", title: "Security & Credentials", content: `

How Your Passwords Are Stored

Nexus Terminal uses a two-tier approach to keep your credentials safe:

  1. OS keychain (preferred) — your operating system's built-in credential store (macOS Keychain, Windows Credential Manager, or Linux Secret Service). If available, this is used automatically.
  2. Encrypted vault (fallback) — if the OS keychain is not available, Nexus Terminal creates a local vault encrypted with AES-256-GCM. You set a master password to unlock it.

Encryption Details

The vault uses Argon2id to derive the encryption key from your master password (128 MiB memory, making brute-force attacks very expensive). Each secret is individually encrypted and never stored in plain text.

TOTP / 2FA Secrets

TOTP secrets for SSH two-factor authentication are stored alongside your other credentials — encrypted in the OS keychain or the file vault. They are never saved in the profile database in plain text.

Known Hosts

When you connect to an SSH server for the first time, Nexus Terminal records the server's fingerprint. If the fingerprint changes on a subsequent connection, you'll be warned — this protects against man-in-the-middle attacks.

You can manage known hosts and change the verification policy in Settings → Known Hosts.

Encrypted Export

When exporting profiles, you can choose encrypted export (.nexus file) which protects the file with a password. This uses the same strong AES-256-GCM encryption as the vault.

`, }, { id: "shortcuts", title: "Keyboard Shortcuts", content: `

On macOS, use ⌘ (Cmd). On Windows and Linux, use Ctrl.

⌘KOpen the command palette
⌘NNew connection
⌘WClose the active tab
⌘TabNext tab
⌘⇧TabPrevious tab
⌘1–⌘9Switch to tab by number
⌘,Open Settings
⌘FSearch in terminal
⌘⇧BToggle broadcast input mode
⌘⇧DSplit pane vertically
⌘⇧ESplit pane horizontally
⌘⇧QQuick connect (no saved profile)
⌘⇧RToggle session recording
⌘⇧SOpen snippet library
F11Toggle fullscreen (Windows/Linux)
`, }, { id: "import-export", title: "Import & Export", content: `

Exporting Profiles

Click the ↗ export button in the sidebar footer. You can choose which profiles to include, and set a password to encrypt the file. Exported files use the .nexus extension.

Importing Profiles

Click the ↙ import button. Select a .nexus file and enter the password used when exporting. Imported profiles will appear in your sidebar immediately.

Importing from OpenSSH

If you have an existing ~/.ssh/config file, go to Settings → About and click Import ~/.ssh/config. Nexus Terminal will create profiles from your SSH Host blocks, including hostnames, usernames, ports, identity files, and jump hosts.

`, }, { id: "troubleshooting", title: "Troubleshooting", content: `

I can't connect to my server

SSH connection fails through a proxy

TOTP/2FA code is rejected

Login script times out

Auto-reconnect keeps failing

VNC will not connect

The terminal looks garbled or the wrong size

Leviathan cannot load, save, or run a tool

"Host key verification failed"

This means the server's fingerprint has changed since your last connection. This can happen if the server was reinstalled, but it could also indicate a security threat. If you're certain it's safe, go to Settings → Known Hosts, find the entry, and remove it. You'll be prompted to accept the new key on your next connection.

I forgot my vault master password

Unfortunately, the vault encryption is designed so that without the master password, the stored credentials cannot be recovered. You would need to delete the vault file and set up a new one. Your connection profiles (without saved passwords) will remain intact.

SFTP panel doesn't open

SFTP is only available for SSH connections (not Telnet, RAW, or GitHub). Make sure you are connected to an SSH session first, then click the 📁 icon on the tab.

The application looks blurry on my display

Make sure your system display scaling is set correctly. On macOS, Nexus Terminal is fully Retina-aware. On Windows, check your DPI settings in Display preferences.

Export/import isn't working

Ensure you're using the same password to import that was used to export. The encrypted .nexus files cannot be opened with the wrong password.

`, }, { id: "fun", title: "Fun Things to Try", content: `

Nexus Terminal isn't just for work! Here are some entertaining things you can connect to:

🌟 Star Wars in ASCII Art

Create a Telnet connection to towel.blinkenlights.nl on port 23. Sit back and enjoy the original Star Wars film retold entirely in ASCII art.

🗺️ MapSCII — World Map in Your Terminal

Connect via SSH to mapscii.me on port 22. Use the arrow keys to pan and a/z to zoom in and out of a fully interactive world map rendered in text.

🎮 Telnet BBS Systems

Connect via Telnet to particlesbbs.dyndns.org on port 23 for a retro bulletin board system experience, complete with door games, message boards, and ASCII art.

🐄 Fortune Cookies

Connect via Telnet to djxmmx.net on port 17 (the Quote of the Day protocol). You'll receive a random quote each time you connect — great for testing RAW TCP connections too.

🔒 Test Your SSH Setup

Connect via SSH to ssh.github.com on port 443 (or github.com on port 22) with your GitHub username. GitHub will greet you by name if your key is set up correctly — a quick way to verify your SSH configuration.

📡 RAW TCP Experiments

Create a RAW TCP connection to any HTTP server on port 80, then type GET / HTTP/1.0 followed by two blank lines. You'll see the raw HTTP response — a great way to understand how the web works under the hood.

📢 Broadcast Mode Fun

Open two or more sessions (e.g. to different servers) and press ⌘⇧B / Ctrl+Shift+B to turn on broadcast mode. Everything you type goes to all sessions at once — try running hostname and watch them all respond with different names!

`, }, ]; body.innerHTML = ""; // Navigation sidebar body.classList.add("help-layout"); var nav = createElement("div", "help-nav"); var content = createElement("div", "help-content"); for (var i = 0; i < HELP_SECTIONS.length; i++) { var section = HELP_SECTIONS[i]; var navItem = createElement("button", "help-nav-item", { textContent: section.title, dataset: { section: section.id }, }); navItem.addEventListener("mousedown", function (e) { e.preventDefault(); }); (function (sec) { navItem.addEventListener("click", function () { nav.querySelectorAll(".help-nav-item").forEach(function (n) { n.classList.remove("active"); }); this.classList.add("active"); content.innerHTML = '

' + sec.title + "

" + sec.content; }); })(section); nav.appendChild(navItem); } body.appendChild(nav); body.appendChild(content); // Show first section by default if (nav.firstChild) { nav.firstChild.classList.add("active"); content.innerHTML = '

' + HELP_SECTIONS[0].title + "

" + HELP_SECTIONS[0].content; } } /** * About tab showing application info. * @param {HTMLElement} body * @private */ _renderAboutTab(body) { body.innerHTML = `

Nexus Terminal

Version …

Developed by Andy Dixon - www.dixon.cx/nexus

`; const versionEl = body.querySelector("[data-role='app-version']"); if (versionEl) { if (window.NexusTerminal?.wailsService) { window.NexusTerminal.wailsService.GetAppInfo() .then((info) => { const version = (info && info.version) || "dev"; versionEl.textContent = `Version ${version}`; }) .catch(() => { versionEl.textContent = "Version unavailable"; }); } else { versionEl.textContent = "Version unavailable"; } } const updateBtn = body.querySelector("#settings-check-update"); if (updateBtn) { updateBtn.addEventListener("click", async () => { updateBtn.disabled = true; updateBtn.textContent = "Checking…"; try { const info = await window.NexusTerminal.wailsService.CheckForUpdates(); const resultEl = body.querySelector("#settings-update-result"); if (this.app && typeof this.app.setUpdateInfo === "function") { this.app.setUpdateInfo(info); } if (info.available) { resultEl.textContent = ""; const updateText = createElement("span"); updateText.style.color = "var(--accent)"; updateText.appendChild(document.createTextNode("Update available: ")); const versionText = createElement("strong"); versionText.textContent = String(info.latestVersion || ""); updateText.appendChild(versionText); resultEl.appendChild(updateText); if (info.releaseURL) { resultEl.appendChild(document.createTextNode(" - ")); const releaseLink = createElement("a", "", { href: "#", textContent: "View release", }); releaseLink.style.color = "var(--accent)"; releaseLink.addEventListener("click", (e) => { e.preventDefault(); if (window.runtime && window.runtime.BrowserOpenURL) { window.runtime.BrowserOpenURL(String(info.releaseURL)); } }); resultEl.appendChild(releaseLink); } } else { resultEl.textContent = "You are running the latest version."; } } catch (err) { const resultEl = body.querySelector("#settings-update-result"); resultEl.textContent = "Update check failed: " + err; } finally { updateBtn.disabled = false; updateBtn.textContent = "Check for Updates"; } }); } const importBtn = body.querySelector("#settings-import-sshconfig"); if (importBtn) { importBtn.addEventListener("click", async () => { importBtn.disabled = true; importBtn.textContent = "Importing…"; try { const count = await window.NexusTerminal.wailsService.ImportSSHConfig( "", "SSH Config", ); this.app.notifications.success( `Imported ${count} host(s) from ~/.ssh/config`, ); await this.app.loadProfiles(); } catch (err) { this.app.notifications.error("SSH config import failed: " + err); } finally { importBtn.disabled = false; importBtn.textContent = "Import ~/.ssh/config"; } }); } this._attachAboutKonamiListener(); } // ── Form helpers ───────────────────────────────────────────────── _attachAboutKonamiListener() { this._detachAboutKonamiListener(); this._konamiIndex = 0; this._aboutKonamiHandler = (event) => { if (!this._overlay || this._activeTab !== "about") return; if (event.ctrlKey || event.metaKey || event.altKey) { this._konamiIndex = 0; return; } const key = event.key.length === 1 ? event.key.toLowerCase() : event.key; if (key === KONAMI_SEQUENCE[this._konamiIndex]) { event.preventDefault(); this._konamiIndex += 1; if (this._konamiIndex === KONAMI_SEQUENCE.length) { this._konamiIndex = 0; void this._toggleLeviathanFromKonami(); } return; } this._konamiIndex = key === KONAMI_SEQUENCE[0] ? 1 : 0; }; window.addEventListener("keydown", this._aboutKonamiHandler); } _detachAboutKonamiListener() { if (this._aboutKonamiHandler) { window.removeEventListener("keydown", this._aboutKonamiHandler); this._aboutKonamiHandler = null; } this._konamiIndex = 0; } async _toggleLeviathanFromKonami() { if (this._konamiTogglePending) return; this._konamiTogglePending = true; const enabled = this.app.state.preferences?.leviathanEnabled !== true; const prefs = { ...this.app.state.preferences, leviathanEnabled: enabled, }; try { await window.NexusTerminal.wailsService.SavePreferences(prefs); this._prefs.leviathanEnabled = enabled; this.app.state.setPreferences(prefs); this.app._applyPreferences?.(); this.app.notifications.success( enabled ? "Leviathan enabled" : "Leviathan disabled", ); } catch (err) { this.app.notifications.error( "Failed to update Leviathan setting: " + err, ); } finally { this._konamiTogglePending = false; } } /** * Renders a single settings form field. * @param {string} type * @param {string} name * @param {string} label * @param {*} value * @param {Object} [opts] * @returns {HTMLElement} * @private */ _settingField(type, name, label, value, opts = {}) { const group = createElement("div", "form-group settings-field"); const labelEl = createElement("label", "form-label", { textContent: label, for: `setting-${name}`, }); group.appendChild(labelEl); let input; switch (type) { case "text": input = createElement("input", "form-input", { type: "text", id: `setting-${name}`, name: `setting-${name}`, value: value || "", placeholder: opts.placeholder || "", }); break; case "number": input = createElement("input", "form-input", { type: "number", id: `setting-${name}`, name: `setting-${name}`, value: value != null ? String(value) : "", }); if (opts.min != null) input.min = opts.min; if (opts.max != null) input.max = opts.max; break; case "select": input = createElement("select", "form-select", { id: `setting-${name}`, name: `setting-${name}`, }); for (const opt of opts.options || []) { const option = createElement("option", "", { value: opt.value, textContent: opt.label, }); if (opt.value === value) option.selected = true; input.appendChild(option); } break; case "toggle": { const wrapper = createElement("div", "form-toggle-wrapper"); input = createElement("input", "form-toggle", { type: "checkbox", id: `setting-${name}`, name: `setting-${name}`, }); input.checked = !!value; const slider = createElement("label", "form-toggle-label", { for: `setting-${name}`, }); wrapper.appendChild(input); wrapper.appendChild(slider); group.appendChild(wrapper); return group; } default: input = createElement("input", "form-input", { type: "text", id: `setting-${name}`, name: `setting-${name}`, value: value || "", }); } group.appendChild(input); return group; } /** * Reads current form field values back into the working preferences copy. * @private */ _collectValues() { if (!this._overlay) return; const val = (name) => { const el = this._overlay.querySelector(`[name="setting-${name}"]`); if (!el) return undefined; if (el.type === "checkbox") return el.checked; if (el.type === "number") return el.value !== "" ? Number(el.value) : 0; return el.value; }; const fields = [ "theme", "terminalTheme", "defaultProtocol", "confirmOnClose", "confirmOnDisconnect", "showStatusBar", "sidebarWidth", "defaultFontFamily", "defaultFontSize", "defaultScrollback", "defaultCursorStyle", "useNativePaste", "localShell", ]; for (const field of fields) { const v = val(field); if (v !== undefined) { this._prefs[field] = v; } } } } window.NexusTerminal = window.NexusTerminal || {}; window.NexusTerminal.Settings = Settings; })();