Convert ES modules to plain scripts using window.NexusTerminal namespace
Replace ES module import/export with a global namespace pattern to avoid silent failures in the Wails v2 WKWebView which does not reliably support <script type="module"> tags. - Remove all import/export statements from JS files - Register each module's exports on window.NexusTerminal - Destructure dependencies from window.NexusTerminal at file top - Load all scripts in dependency order in index.html as plain <script> tags
This commit is contained in:
+17
-37
@@ -51,40 +51,8 @@
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav" id="sidebar-nav">
|
||||
<!-- Favourites -->
|
||||
<div class="sidebar-section" id="favourites-section">
|
||||
<button class="sidebar-section-header" data-section="favourites">
|
||||
<span class="section-icon">★</span>
|
||||
<span>Favourites</span>
|
||||
<span class="section-count" id="favourites-count">0</span>
|
||||
</button>
|
||||
<div class="sidebar-list" id="favourites-list">
|
||||
<div class="sidebar-list-empty">No favourites yet</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent -->
|
||||
<div class="sidebar-section" id="recent-section">
|
||||
<button class="sidebar-section-header" data-section="recent">
|
||||
<span class="section-icon">⟳</span>
|
||||
<span>Recent</span>
|
||||
</button>
|
||||
<div class="sidebar-list" id="recent-list">
|
||||
<div class="sidebar-list-empty">No recent sessions</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- All Connections -->
|
||||
<div class="sidebar-section" id="all-section">
|
||||
<button class="sidebar-section-header" data-section="all">
|
||||
<span class="section-icon">⊞</span>
|
||||
<span>All Connections</span>
|
||||
<span class="section-count" id="all-count">0</span>
|
||||
</button>
|
||||
<div class="sidebar-list" id="all-list">
|
||||
<div class="sidebar-list-empty">No saved connections</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dynamically managed by sidebar.js -->
|
||||
<div id="sidebar-profiles" class="sidebar-profiles"></div>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
@@ -162,7 +130,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Session containers are injected here dynamically -->
|
||||
<!-- Session panels are injected here dynamically by tabs.js -->
|
||||
<div id="panel-container" class="panel-container"></div>
|
||||
</main>
|
||||
|
||||
<!-- ============================================================
|
||||
@@ -192,7 +161,18 @@
|
||||
<script src="/src/js/lib/xterm-addon-search.min.js"></script>
|
||||
<script src="/src/js/lib/xterm-addon-web-links.min.js"></script>
|
||||
|
||||
<!-- Application entry point -->
|
||||
<script type="module" src="/src/js/app.js"></script>
|
||||
<!-- Application scripts (loaded in dependency order) -->
|
||||
<script src="/src/js/utils.js"></script>
|
||||
<script src="/src/js/state.js"></script>
|
||||
<script src="/src/js/notifications.js"></script>
|
||||
<script src="/src/js/sidebar.js"></script>
|
||||
<script src="/src/js/tabs.js"></script>
|
||||
<script src="/src/js/terminal.js"></script>
|
||||
<script src="/src/js/profiles.js"></script>
|
||||
<script src="/src/js/raw.js"></script>
|
||||
<script src="/src/js/settings.js"></script>
|
||||
<script src="/src/js/commands.js"></script>
|
||||
<script src="/src/js/secrets.js"></script>
|
||||
<script src="/src/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+43
-15
@@ -3,17 +3,7 @@
|
||||
* Initialises the application shell, loads profiles, and sets up event listeners.
|
||||
*/
|
||||
|
||||
import { State } from './state.js';
|
||||
import { Sidebar } from './sidebar.js';
|
||||
import { TabManager } from './tabs.js';
|
||||
import { TerminalManager } from './terminal.js';
|
||||
import { ProfileEditor } from './profiles.js';
|
||||
import { RawViewer } from './raw.js';
|
||||
import { Notifications } from './notifications.js';
|
||||
import { Settings } from './settings.js';
|
||||
import { CommandPalette } from './commands.js';
|
||||
import { SecretPrompt } from './secrets.js';
|
||||
import { stateLabel } from './utils.js';
|
||||
const { State, Sidebar, TabManager, TerminalManager, ProfileEditor, RawViewer, Notifications, Settings, CommandPalette, SecretPrompt, stateLabel } = window.NexusTerminal;
|
||||
|
||||
class App {
|
||||
constructor() {
|
||||
@@ -31,18 +21,51 @@ class App {
|
||||
|
||||
/**
|
||||
* Bootstraps the application after the DOM is ready.
|
||||
* UI event binding happens synchronously so buttons always work.
|
||||
* Backend data loading waits for the Wails runtime to be available.
|
||||
*/
|
||||
async init() {
|
||||
// Phase 1 — bind all UI event handlers immediately (DOM-only, no backend).
|
||||
this.setupUI();
|
||||
this.setupKeyboard();
|
||||
this.updateStatusBar();
|
||||
|
||||
// Phase 2 — wait for the Wails runtime, then load backend data and bind events.
|
||||
await this._waitForWailsRuntime();
|
||||
this.setupEvents();
|
||||
|
||||
await this.loadProfiles();
|
||||
await this.loadPreferences();
|
||||
await this.checkSecretStore();
|
||||
|
||||
this.setupEvents();
|
||||
this.setupKeyboard();
|
||||
this.setupUI();
|
||||
this.updateStatusBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls for `window.go` and `window.runtime` to become available.
|
||||
* Wails injects these into the webview; on fast machines they are
|
||||
* present before DOMContentLoaded, but on slower starts there may
|
||||
* be a brief delay.
|
||||
* @returns {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
_waitForWailsRuntime() {
|
||||
return new Promise((resolve) => {
|
||||
const maxWait = 5000;
|
||||
const start = Date.now();
|
||||
const check = () => {
|
||||
if (window.go && window.runtime) {
|
||||
resolve();
|
||||
} else if (Date.now() - start < maxWait) {
|
||||
setTimeout(check, 50);
|
||||
} else {
|
||||
console.warn('Wails runtime not detected after ' + maxWait + 'ms — proceeding anyway');
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
// ── Data loading ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -94,8 +117,13 @@ class App {
|
||||
|
||||
/**
|
||||
* Registers listeners for backend events emitted via the Wails runtime.
|
||||
* Guarded: silently skips if the runtime is unavailable.
|
||||
*/
|
||||
setupEvents() {
|
||||
if (!window.runtime || typeof window.runtime.EventsOn !== 'function') {
|
||||
console.warn('window.runtime not available — Wails event listeners not bound');
|
||||
return;
|
||||
}
|
||||
// Incoming session data (terminal output)
|
||||
window.runtime.EventsOn('session:data', (payload) => {
|
||||
this.terminals.handleData(payload.sessionID, payload.data);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* profile connections, and utility actions.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml, fuzzyMatch, $ } from './utils.js';
|
||||
const { createElement, escapeHtml, fuzzyMatch, $ } = window.NexusTerminal;
|
||||
|
||||
export class CommandPalette {
|
||||
class CommandPalette {
|
||||
/**
|
||||
* @param {import('./app.js').App} app
|
||||
*/
|
||||
@@ -379,3 +379,6 @@ export class CommandPalette {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.CommandPalette = CommandPalette;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* in the top-right corner of the viewport.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml } from './utils.js';
|
||||
const { createElement, escapeHtml } = window.NexusTerminal;
|
||||
|
||||
/** Map of notification type to SVG icon path data. */
|
||||
const ICONS = {
|
||||
@@ -14,7 +14,7 @@ const ICONS = {
|
||||
info: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
||||
};
|
||||
|
||||
export class Notifications {
|
||||
class Notifications {
|
||||
constructor() {
|
||||
/** @type {HTMLElement} */
|
||||
this.container = document.getElementById('notifications');
|
||||
@@ -122,3 +122,6 @@ export class Notifications {
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.Notifications = Notifications;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Dynamically switches form sections based on the selected protocol.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml, generateId, $ } from './utils.js';
|
||||
const { createElement, escapeHtml, generateId, $ } = window.NexusTerminal;
|
||||
|
||||
/**
|
||||
* Default values mirroring the Go backend defaults.
|
||||
@@ -53,7 +53,7 @@ const DEFAULTS = {
|
||||
},
|
||||
};
|
||||
|
||||
export class ProfileEditor {
|
||||
class ProfileEditor {
|
||||
/**
|
||||
* @param {import('./app.js').App} app
|
||||
*/
|
||||
@@ -886,3 +886,6 @@ export class ProfileEditor {
|
||||
return Object.entries(envVars).map(([k, v]) => `${k}=${v}`).join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.ProfileEditor = ProfileEditor;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* along with a send bar, pause/resume, and capture saving.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml, formatTimestamp, formatBytes } from './utils.js';
|
||||
const { createElement, escapeHtml, formatTimestamp, formatBytes } = window.NexusTerminal;
|
||||
|
||||
/**
|
||||
* Maximum number of data entries retained in the scrollback buffer.
|
||||
@@ -12,7 +12,7 @@ import { createElement, escapeHtml, formatTimestamp, formatBytes } from './utils
|
||||
*/
|
||||
const MAX_BUFFER_ENTRIES = 5000;
|
||||
|
||||
export class RawViewer {
|
||||
class RawViewer {
|
||||
/**
|
||||
* @param {import('./app.js').App} app
|
||||
*/
|
||||
@@ -562,3 +562,6 @@ export class RawViewer {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.RawViewer = RawViewer;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* and SSH host-key verification confirmations.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml } from './utils.js';
|
||||
const { createElement, escapeHtml } = window.NexusTerminal;
|
||||
|
||||
export class SecretPrompt {
|
||||
class SecretPrompt {
|
||||
constructor(app) {
|
||||
/** @type {import('./app.js').App} */
|
||||
this.app = app;
|
||||
@@ -298,3 +298,6 @@ export class SecretPrompt {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.SecretPrompt = SecretPrompt;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* terminal defaults, security, known-hosts management, and about info.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml, formatTimestamp, $ } from './utils.js';
|
||||
const { createElement, escapeHtml, formatTimestamp, $ } = window.NexusTerminal;
|
||||
|
||||
/** Settings tab definitions. */
|
||||
const TABS = [
|
||||
@@ -15,7 +15,7 @@ const TABS = [
|
||||
{ key: 'about', label: 'About' },
|
||||
];
|
||||
|
||||
export class Settings {
|
||||
class Settings {
|
||||
/**
|
||||
* @param {import('./app.js').App} app
|
||||
*/
|
||||
@@ -461,3 +461,6 @@ export class Settings {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.Settings = Settings;
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
* and context menus.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml, fuzzyMatch, protocolColour, dismissContextMenus, truncate } from './utils.js';
|
||||
const { createElement, escapeHtml, fuzzyMatch, protocolColour, dismissContextMenus, truncate } = window.NexusTerminal;
|
||||
|
||||
/** Maximum number of profiles shown in the "Recent" section. */
|
||||
const RECENT_LIMIT = 5;
|
||||
|
||||
export class Sidebar {
|
||||
class Sidebar {
|
||||
/**
|
||||
* @param {import('./app.js').App} app
|
||||
*/
|
||||
@@ -333,3 +333,6 @@ export class Sidebar {
|
||||
.map((r) => r.profile);
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.Sidebar = Sidebar;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* emitting change events so the UI stays in sync.
|
||||
*/
|
||||
|
||||
export class State {
|
||||
class State {
|
||||
constructor() {
|
||||
/** @type {Array<Object>} */
|
||||
this.profiles = [];
|
||||
@@ -223,3 +223,6 @@ export class State {
|
||||
this.emit('secretStore:init', initialised);
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.State = State;
|
||||
|
||||
+24
-18
@@ -4,16 +4,19 @@
|
||||
* pinning tabs, and context menus.
|
||||
*/
|
||||
|
||||
import { createElement, escapeHtml, protocolColour, stateLabel, dismissContextMenus } from './utils.js';
|
||||
const { createElement, escapeHtml, protocolColour, stateLabel, dismissContextMenus } = window.NexusTerminal;
|
||||
|
||||
export class TabManager {
|
||||
class TabManager {
|
||||
/**
|
||||
* @param {import('./app.js').App} app
|
||||
*/
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
|
||||
/** @type {HTMLElement} The tab bar container. */
|
||||
/** @type {HTMLElement} The inner tab list where tab elements are inserted. */
|
||||
this.tabList = document.getElementById('tab-list');
|
||||
|
||||
/** @type {HTMLElement} The outer tab bar wrapper (used for drag-and-drop). */
|
||||
this.tabBar = document.getElementById('tab-bar');
|
||||
|
||||
/** @type {HTMLElement} The container for terminal/viewer panels. */
|
||||
@@ -56,7 +59,7 @@ export class TabManager {
|
||||
if (idx !== -1) this.order.splice(idx, 1);
|
||||
|
||||
// Remove tab element
|
||||
const tabEl = this.tabBar.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
if (tabEl) tabEl.remove();
|
||||
|
||||
// Remove panel
|
||||
@@ -86,7 +89,7 @@ export class TabManager {
|
||||
this.app.state.setActiveSession(sessionId);
|
||||
|
||||
// Highlight active tab
|
||||
this.tabBar.querySelectorAll('.tab').forEach((t) => {
|
||||
this.tabList.querySelectorAll('.tab').forEach((t) => {
|
||||
t.classList.toggle('tab-active', t.dataset.sessionId === sessionId);
|
||||
});
|
||||
|
||||
@@ -146,7 +149,7 @@ export class TabManager {
|
||||
*/
|
||||
renameTab(sessionId, name) {
|
||||
this.app.state.updateSession(sessionId, { tabName: name });
|
||||
const tabEl = this.tabBar.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
if (tabEl) {
|
||||
tabEl.querySelector('.tab-name').textContent = name;
|
||||
}
|
||||
@@ -216,7 +219,7 @@ export class TabManager {
|
||||
updateSessionState(sessionId, state, error) {
|
||||
this.app.state.updateSession(sessionId, { state, error: error || '' });
|
||||
|
||||
const tabEl = this.tabBar.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
if (!tabEl) return;
|
||||
|
||||
const dot = tabEl.querySelector('.tab-status');
|
||||
@@ -383,7 +386,7 @@ export class TabManager {
|
||||
this.showContextMenu(sessionInfo.id, e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
this.tabBar.appendChild(tab);
|
||||
this.tabList.appendChild(tab);
|
||||
}
|
||||
|
||||
// ── Internal helpers ─────────────────────────────────────────────
|
||||
@@ -405,8 +408,8 @@ export class TabManager {
|
||||
});
|
||||
|
||||
for (const id of sorted) {
|
||||
const el = this.tabBar.querySelector(`[data-session-id="${id}"]`);
|
||||
if (el) this.tabBar.appendChild(el);
|
||||
const el = this.tabList.querySelector(`[data-session-id="${id}"]`);
|
||||
if (el) this.tabList.appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +420,7 @@ export class TabManager {
|
||||
*/
|
||||
_updateTabStyles(sessionId) {
|
||||
const session = this.app.state.getSession(sessionId);
|
||||
const tabEl = this.tabBar.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`);
|
||||
if (tabEl && session) {
|
||||
tabEl.classList.toggle('tab-pinned', !!session.pinned);
|
||||
}
|
||||
@@ -484,11 +487,11 @@ export class TabManager {
|
||||
* @private
|
||||
*/
|
||||
_setupDragAndDrop() {
|
||||
if (!this.tabBar) return;
|
||||
if (!this.tabList) return;
|
||||
|
||||
let draggedId = null;
|
||||
|
||||
this.tabBar.addEventListener('dragstart', (e) => {
|
||||
this.tabList.addEventListener('dragstart', (e) => {
|
||||
const tab = e.target.closest('.tab');
|
||||
if (!tab) return;
|
||||
draggedId = tab.dataset.sessionId;
|
||||
@@ -496,7 +499,7 @@ export class TabManager {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
});
|
||||
|
||||
this.tabBar.addEventListener('dragover', (e) => {
|
||||
this.tabList.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
|
||||
@@ -509,14 +512,14 @@ export class TabManager {
|
||||
target.classList.toggle('tab-drop-after', e.clientX >= midX);
|
||||
});
|
||||
|
||||
this.tabBar.addEventListener('dragleave', (e) => {
|
||||
this.tabList.addEventListener('dragleave', (e) => {
|
||||
const target = e.target.closest('.tab');
|
||||
if (target) {
|
||||
target.classList.remove('tab-drop-before', 'tab-drop-after');
|
||||
}
|
||||
});
|
||||
|
||||
this.tabBar.addEventListener('drop', (e) => {
|
||||
this.tabList.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
const target = e.target.closest('.tab');
|
||||
if (!target || !draggedId) return;
|
||||
@@ -536,11 +539,14 @@ export class TabManager {
|
||||
this._reorderDOM();
|
||||
});
|
||||
|
||||
this.tabBar.addEventListener('dragend', () => {
|
||||
this.tabBar.querySelectorAll('.tab').forEach((t) => {
|
||||
this.tabList.addEventListener('dragend', () => {
|
||||
this.tabList.querySelectorAll('.tab').forEach((t) => {
|
||||
t.classList.remove('tab-dragging', 'tab-drop-before', 'tab-drop-after');
|
||||
});
|
||||
draggedId = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.TabManager = TabManager;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
/* global Terminal, FitAddon, SearchAddon, WebLinksAddon */
|
||||
|
||||
export class TerminalManager {
|
||||
class TerminalManager {
|
||||
/**
|
||||
* @param {import('./app.js').App} app
|
||||
*/
|
||||
@@ -284,3 +284,6 @@ export class TerminalManager {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.TerminalManager = TerminalManager;
|
||||
|
||||
+18
-15
@@ -8,7 +8,7 @@
|
||||
* @param {string} selector - CSS selector.
|
||||
* @returns {Element|null}
|
||||
*/
|
||||
export function $(selector) {
|
||||
function $(selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export function $(selector) {
|
||||
* @param {string} selector - CSS selector.
|
||||
* @returns {NodeListOf<Element>}
|
||||
*/
|
||||
export function $$(selector) {
|
||||
function $$(selector) {
|
||||
return document.querySelectorAll(selector);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export function $$(selector) {
|
||||
* @param {Object} [attrs] - Attribute key/value pairs.
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
export function createElement(tag, className, attrs = {}) {
|
||||
function createElement(tag, className, attrs = {}) {
|
||||
const el = document.createElement(tag);
|
||||
if (className) {
|
||||
el.className = className;
|
||||
@@ -56,7 +56,7 @@ export function createElement(tag, className, attrs = {}) {
|
||||
* @param {string} str - Raw string.
|
||||
* @returns {string} Escaped string safe for innerHTML.
|
||||
*/
|
||||
export function escapeHtml(str) {
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
const map = {
|
||||
'&': '&',
|
||||
@@ -73,7 +73,7 @@ export function escapeHtml(str) {
|
||||
* @param {Date|string} date
|
||||
* @returns {string} e.g. "14:32:07" or "2 Jan 14:32".
|
||||
*/
|
||||
export function formatTimestamp(date) {
|
||||
function formatTimestamp(date) {
|
||||
if (!date) return '';
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
if (isNaN(d.getTime())) return '';
|
||||
@@ -98,7 +98,7 @@ export function formatTimestamp(date) {
|
||||
* @param {number} ms - Delay in milliseconds.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function debounce(fn, ms) {
|
||||
function debounce(fn, ms) {
|
||||
let timer = null;
|
||||
return function (...args) {
|
||||
clearTimeout(timer);
|
||||
@@ -113,7 +113,7 @@ export function debounce(fn, ms) {
|
||||
* @param {string} query
|
||||
* @returns {{ match: boolean, score: number }}
|
||||
*/
|
||||
export function fuzzyMatch(text, query) {
|
||||
function fuzzyMatch(text, query) {
|
||||
if (!query) return { match: true, score: 1 };
|
||||
const lowerText = text.toLowerCase();
|
||||
const lowerQuery = query.toLowerCase();
|
||||
@@ -146,7 +146,7 @@ export function fuzzyMatch(text, query) {
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export function base64Encode(str) {
|
||||
function base64Encode(str) {
|
||||
return btoa(str);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ export function base64Encode(str) {
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export function base64Decode(str) {
|
||||
function base64Decode(str) {
|
||||
return atob(str);
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ export function base64Decode(str) {
|
||||
* Generates a new UUID v4 using the browser crypto API.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function generateId() {
|
||||
function generateId() {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ export function generateId() {
|
||||
* @param {number} bytes
|
||||
* @returns {string} e.g. "1.23 KB"
|
||||
*/
|
||||
export function formatBytes(bytes) {
|
||||
function formatBytes(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
const k = 1024;
|
||||
@@ -187,7 +187,7 @@ export function formatBytes(bytes) {
|
||||
* @param {number} len
|
||||
* @returns {string}
|
||||
*/
|
||||
export function truncate(str, len) {
|
||||
function truncate(str, len) {
|
||||
if (!str || str.length <= len) return str || '';
|
||||
return str.slice(0, len) + '…';
|
||||
}
|
||||
@@ -195,7 +195,7 @@ export function truncate(str, len) {
|
||||
/**
|
||||
* Removes any open context menus from the DOM.
|
||||
*/
|
||||
export function dismissContextMenus() {
|
||||
function dismissContextMenus() {
|
||||
document.querySelectorAll('.context-menu').forEach((m) => m.remove());
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ export function dismissContextMenus() {
|
||||
* @param {string} protocol - 'ssh', 'telnet', or 'raw'.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function protocolColour(protocol) {
|
||||
function protocolColour(protocol) {
|
||||
switch (protocol) {
|
||||
case 'ssh': return 'protocol-ssh';
|
||||
case 'telnet': return 'protocol-telnet';
|
||||
@@ -218,7 +218,7 @@ export function protocolColour(protocol) {
|
||||
* @param {string} state
|
||||
* @returns {string}
|
||||
*/
|
||||
export function stateLabel(state) {
|
||||
function stateLabel(state) {
|
||||
switch (state) {
|
||||
case 'connecting': return 'Connecting';
|
||||
case 'connected': return 'Connected';
|
||||
@@ -227,3 +227,6 @@ export function stateLabel(state) {
|
||||
default: return state || 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
Object.assign(window.NexusTerminal, { $, $$, createElement, escapeHtml, formatTimestamp, debounce, fuzzyMatch, base64Encode, base64Decode, generateId, formatBytes, truncate, dismissContextMenus, protocolColour, stateLabel });
|
||||
|
||||
Reference in New Issue
Block a user