refactor: improve code readability and formatting in profiles.js and tabs.js

- Updated formatting for better readability, including consistent use of line breaks and indentation.
- Enhanced the clarity of event listener functions in profiles.js.
- Refactored tab management logic in tabs.js for improved maintainability.
- Ensured consistent use of single quotes and double quotes across the files.
This commit is contained in:
Andy Dixon
2026-04-22 14:57:01 +01:00
parent 8fa9fb5be5
commit 98ca4743e5
6 changed files with 1083 additions and 1002 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ Prefer a one-off session? Use **Cmd+Shift+Q** / **Ctrl+Shift+Q** to quick connec
All shortcuts use **Cmd** on macOS and **Ctrl** on Windows/Linux. All shortcuts use **Cmd** on macOS and **Ctrl** on Windows/Linux.
| macOS Shortcut | Windows/Linux Shortcut | Action | | macOS Shortcut | Windows/Linux Shortcut | Action |
| ------------- | ----------------------- | --------------------------------- | | ----------------- | ---------------------- | --------------------------- |
| Cmd + K | Ctrl + K | Open command palette | | Cmd + K | Ctrl + K | Open command palette |
| Cmd + N | Ctrl + N | New connection | | Cmd + N | Ctrl + N | New connection |
| Cmd + W | Ctrl + W | Close current tab | | Cmd + W | Ctrl + W | Close current tab |
+1 -1
View File
@@ -43,7 +43,7 @@ The upload picker lists folders only and marks permissions:
## Common shortcuts ## Common shortcuts
| macOS Shortcut | Windows/Linux Shortcut | Action | | macOS Shortcut | Windows/Linux Shortcut | Action |
| ------------- | ----------------------- | ------------------------ | | --------------- | ---------------------- | ------------------------ |
| Cmd + K | Ctrl + K | Command palette | | Cmd + K | Ctrl + K | Command palette |
| Cmd + N | Ctrl + N | New connection | | Cmd + N | Ctrl + N | New connection |
| Cmd + W | Ctrl + W | Close tab | | Cmd + W | Ctrl + W | Close tab |
+17 -7
View File
@@ -289,7 +289,9 @@
border: 1px dashed var(--border-default); border: 1px dashed var(--border-default);
border-radius: var(--radius-md); border-radius: var(--radius-md);
cursor: pointer; cursor: pointer;
transition: border-color var(--transition-fast), color var(--transition-fast); transition:
border-color var(--transition-fast),
color var(--transition-fast);
} }
.jump-host-add:hover { .jump-host-add:hover {
@@ -334,7 +336,7 @@
} }
.form-group.is-valid .form-input:focus { .form-group.is-valid .form-input:focus {
box-shadow: 0 0 0 2px rgba(102, 187, 106, 0.20); box-shadow: 0 0 0 2px rgba(102, 187, 106, 0.2);
} }
.form-group.is-invalid .form-input { .form-group.is-invalid .form-input {
@@ -342,7 +344,7 @@
} }
.form-group.is-invalid .form-input:focus { .form-group.is-invalid .form-input:focus {
box-shadow: 0 0 0 2px rgba(239, 83, 80, 0.20); box-shadow: 0 0 0 2px rgba(239, 83, 80, 0.2);
} }
.form-validation-icon { .form-validation-icon {
@@ -353,8 +355,12 @@
font-size: var(--text-sm); font-size: var(--text-sm);
} }
.form-group.is-valid .form-validation-icon { color: var(--success); } .form-group.is-valid .form-validation-icon {
.form-group.is-invalid .form-validation-icon { color: var(--error); } color: var(--success);
}
.form-group.is-invalid .form-validation-icon {
color: var(--error);
}
/* ----------------------------------------------------------------------- /* -----------------------------------------------------------------------
Help text under sections Help text under sections
@@ -411,7 +417,9 @@
border: 1px solid var(--border-default); border: 1px solid var(--border-default);
border-left: none; border-left: none;
cursor: pointer; cursor: pointer;
transition: background var(--transition-fast), color var(--transition-fast); transition:
background var(--transition-fast),
color var(--transition-fast);
} }
.form-number-btn:last-child { .form-number-btn:last-child {
@@ -660,7 +668,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: background 0.15s, color 0.15s; transition:
background 0.15s,
color 0.15s;
} }
.login-script-btn:hover { .login-script-btn:hover {
+9 -3
View File
@@ -83,7 +83,9 @@
z-index: 6; z-index: 6;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transition: opacity var(--transition-fast), visibility var(--transition-fast); transition:
opacity var(--transition-fast),
visibility var(--transition-fast);
} }
.terminal-disconnect-overlay.is-visible { .terminal-disconnect-overlay.is-visible {
@@ -229,7 +231,9 @@
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: var(--text-xs); font-size: var(--text-xs);
opacity: 0.5; opacity: 0.5;
transition: opacity var(--transition-fast), color var(--transition-fast); transition:
opacity var(--transition-fast),
color var(--transition-fast);
} }
.terminal-search-toggle.is-active { .terminal-search-toggle.is-active {
@@ -332,7 +336,9 @@
background: var(--bg-surface); background: var(--bg-surface);
border: none; border: none;
cursor: pointer; cursor: pointer;
transition: background var(--transition-fast), color var(--transition-fast); transition:
background var(--transition-fast),
color var(--transition-fast);
} }
.raw-view-toggle-btn:hover { .raw-view-toggle-btn:hover {
+7 -4
View File
@@ -1659,7 +1659,9 @@
<button class="btn btn-icon btn-danger jump-host-remove" data-index="${i}" title="Remove">&times;</button> <button class="btn btn-icon btn-danger jump-host-remove" data-index="${i}" title="Remove">&times;</button>
</div> </div>
`; `;
const keyInput = row.querySelector(`input[name="jumpHost-${i}-privateKeyPath"]`); const keyInput = row.querySelector(
`input[name="jumpHost-${i}-privateKeyPath"]`,
);
const browseBtn = row.querySelector(".jump-host-browse"); const browseBtn = row.querySelector(".jump-host-browse");
const error = createElement("span", "form-error"); const error = createElement("span", "form-error");
error.style.display = "none"; error.style.display = "none";
@@ -1712,7 +1714,9 @@
_attachKeyPathValidation(input, errorEl, successEl) { _attachKeyPathValidation(input, errorEl, successEl) {
if (!input || !errorEl) return; if (!input || !errorEl) return;
input.addEventListener("blur", () => this._validateKeyPath(input, errorEl, successEl)); input.addEventListener("blur", () =>
this._validateKeyPath(input, errorEl, successEl),
);
input.addEventListener("input", () => { input.addEventListener("input", () => {
errorEl.style.display = "none"; errorEl.style.display = "none";
errorEl.textContent = ""; errorEl.textContent = "";
@@ -1757,8 +1761,7 @@
} }
return; return;
} }
return window.go.api.Service.FileIsPrivateKey(value) return window.go.api.Service.FileIsPrivateKey(value).then((isKey) => {
.then((isKey) => {
if (!isKey) { if (!isKey) {
errorEl.textContent = "File is not a recognised private key."; errorEl.textContent = "File is not a recognised private key.";
errorEl.style.display = "block"; errorEl.style.display = "block";
+158 -96
View File
@@ -5,7 +5,13 @@
* pinning tabs, and context menus. * pinning tabs, and context menus.
*/ */
const { createElement, escapeHtml, protocolColour, stateLabel, dismissContextMenus } = window.NexusTerminal; const {
createElement,
escapeHtml,
protocolColour,
stateLabel,
dismissContextMenus,
} = window.NexusTerminal;
class TabManager { class TabManager {
/** /**
@@ -15,13 +21,13 @@ class TabManager {
this.app = app; this.app = app;
/** @type {HTMLElement} The inner tab list where tab elements are inserted. */ /** @type {HTMLElement} The inner tab list where tab elements are inserted. */
this.tabList = document.getElementById('tab-list'); this.tabList = document.getElementById("tab-list");
/** @type {HTMLElement} The outer tab bar wrapper (used for drag-and-drop). */ /** @type {HTMLElement} The outer tab bar wrapper (used for drag-and-drop). */
this.tabBar = document.getElementById('tab-bar'); this.tabBar = document.getElementById("tab-bar");
/** @type {HTMLElement} The container for terminal/viewer panels. */ /** @type {HTMLElement} The container for terminal/viewer panels. */
this.panelContainer = document.getElementById('panel-container'); this.panelContainer = document.getElementById("panel-container");
/** @type {string[]} Ordered list of session IDs. */ /** @type {string[]} Ordered list of session IDs. */
this.order = []; this.order = [];
@@ -40,11 +46,11 @@ class TabManager {
this._renderTab(sessionInfo); this._renderTab(sessionInfo);
// Create panel container for this session's terminal/viewer // Create panel container for this session's terminal/viewer
const panel = createElement('div', 'session-panel', { const panel = createElement("div", "session-panel", {
id: `panel-${sessionInfo.id}`, id: `panel-${sessionInfo.id}`,
dataset: { sessionId: sessionInfo.id }, dataset: { sessionId: sessionInfo.id },
}); });
panel.style.display = 'none'; panel.style.display = "none";
this.panelContainer.appendChild(panel); this.panelContainer.appendChild(panel);
// Hide the empty-state BEFORE activating the panel so the // Hide the empty-state BEFORE activating the panel so the
@@ -62,7 +68,9 @@ class TabManager {
if (idx !== -1) this.order.splice(idx, 1); if (idx !== -1) this.order.splice(idx, 1);
// Remove tab element // Remove tab element
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`); const tabEl = this.tabList.querySelector(
`[data-session-id="${sessionId}"]`,
);
if (tabEl) tabEl.remove(); if (tabEl) tabEl.remove();
// Remove panel // Remove panel
@@ -71,7 +79,8 @@ class TabManager {
// If this was the active tab, activate the nearest neighbour // If this was the active tab, activate the nearest neighbour
if (this.app.state.activeSessionId === sessionId) { if (this.app.state.activeSessionId === sessionId) {
const next = this.order.length > 0 const next =
this.order.length > 0
? this.order[Math.min(idx, this.order.length - 1)] ? this.order[Math.min(idx, this.order.length - 1)]
: null; : null;
if (next) { if (next) {
@@ -92,13 +101,13 @@ class TabManager {
this.app.state.setActiveSession(sessionId); this.app.state.setActiveSession(sessionId);
// Highlight active tab // Highlight active tab
this.tabList.querySelectorAll('.tab').forEach((t) => { this.tabList.querySelectorAll(".tab").forEach((t) => {
t.classList.toggle('tab-active', t.dataset.sessionId === sessionId); t.classList.toggle("tab-active", t.dataset.sessionId === sessionId);
}); });
// Show corresponding panel, hide others // Show corresponding panel, hide others
this.panelContainer.querySelectorAll('.session-panel').forEach((p) => { this.panelContainer.querySelectorAll(".session-panel").forEach((p) => {
p.style.display = p.dataset.sessionId === sessionId ? 'flex' : 'none'; p.style.display = p.dataset.sessionId === sessionId ? "flex" : "none";
}); });
// Notify sidebar // Notify sidebar
@@ -136,7 +145,11 @@ class TabManager {
const session = this.app.state.getSession(sessionId); const session = this.app.state.getSession(sessionId);
// Ask for confirmation if connected and the preference is set // Ask for confirmation if connected and the preference is set
if (session && session.state === 'connected' && this.app.state.preferences.confirmOnDisconnect) { if (
session &&
session.state === "connected" &&
this.app.state.preferences.confirmOnDisconnect
) {
const ok = await this._confirmClose(sessionId); const ok = await this._confirmClose(sessionId);
if (!ok) return; if (!ok) return;
} }
@@ -164,8 +177,8 @@ class TabManager {
const createElement = window.NexusTerminal.createElement; const createElement = window.NexusTerminal.createElement;
const label = this._tabLabel(sessionId); const label = this._tabLabel(sessionId);
return new Promise((resolve) => { return new Promise((resolve) => {
const backdrop = createElement('div', 'modal-backdrop'); const backdrop = createElement("div", "modal-backdrop");
const modal = createElement('div', 'modal modal-sm'); const modal = createElement("div", "modal modal-sm");
modal.innerHTML = ` modal.innerHTML = `
<div class="modal-header"> <div class="modal-header">
<h3 class="modal-title">Close Session</h3> <h3 class="modal-title">Close Session</h3>
@@ -184,20 +197,28 @@ class TabManager {
backdrop.appendChild(modal); backdrop.appendChild(modal);
document.body.appendChild(backdrop); document.body.appendChild(backdrop);
requestAnimationFrame(() => { requestAnimationFrame(() => {
backdrop.classList.add('is-visible'); backdrop.classList.add("is-visible");
modal.classList.add('is-visible'); modal.classList.add("is-visible");
}); });
const close = (val) => { const close = (val) => {
backdrop.classList.remove('is-visible'); backdrop.classList.remove("is-visible");
modal.classList.remove('is-visible'); modal.classList.remove("is-visible");
setTimeout(() => backdrop.remove(), 200); setTimeout(() => backdrop.remove(), 200);
resolve(val); resolve(val);
}; };
modal.querySelector('.modal-close').addEventListener('click', () => close(false)); modal
modal.querySelector('.confirm-cancel').addEventListener('click', () => close(false)); .querySelector(".modal-close")
modal.querySelector('.confirm-ok').addEventListener('click', () => close(true)); .addEventListener("click", () => close(false));
backdrop.addEventListener('click', (e) => { if (e.target === backdrop) close(false); }); modal
.querySelector(".confirm-cancel")
.addEventListener("click", () => close(false));
modal
.querySelector(".confirm-ok")
.addEventListener("click", () => close(true));
backdrop.addEventListener("click", (e) => {
if (e.target === backdrop) close(false);
});
}); });
} }
@@ -208,9 +229,11 @@ class TabManager {
*/ */
renameTab(sessionId, name) { renameTab(sessionId, name) {
this.app.state.updateSession(sessionId, { tabName: name }); this.app.state.updateSession(sessionId, { tabName: name });
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`); const tabEl = this.tabList.querySelector(
`[data-session-id="${sessionId}"]`,
);
if (tabEl) { if (tabEl) {
tabEl.querySelector('.tab-name').textContent = name; tabEl.querySelector(".tab-name").textContent = name;
} }
} }
@@ -241,7 +264,10 @@ class TabManager {
moveLeft(sessionId) { moveLeft(sessionId) {
const idx = this.order.indexOf(sessionId); const idx = this.order.indexOf(sessionId);
if (idx > 0) { if (idx > 0) {
[this.order[idx - 1], this.order[idx]] = [this.order[idx], this.order[idx - 1]]; [this.order[idx - 1], this.order[idx]] = [
this.order[idx],
this.order[idx - 1],
];
this._reorderDOM(); this._reorderDOM();
} }
} }
@@ -253,7 +279,10 @@ class TabManager {
moveRight(sessionId) { moveRight(sessionId) {
const idx = this.order.indexOf(sessionId); const idx = this.order.indexOf(sessionId);
if (idx >= 0 && idx < this.order.length - 1) { if (idx >= 0 && idx < this.order.length - 1) {
[this.order[idx], this.order[idx + 1]] = [this.order[idx + 1], this.order[idx]]; [this.order[idx], this.order[idx + 1]] = [
this.order[idx + 1],
this.order[idx],
];
this._reorderDOM(); this._reorderDOM();
} }
} }
@@ -276,18 +305,20 @@ class TabManager {
* @param {string} [error] * @param {string} [error]
*/ */
updateSessionState(sessionId, state, error) { updateSessionState(sessionId, state, error) {
this.app.state.updateSession(sessionId, { state, error: error || '' }); this.app.state.updateSession(sessionId, { state, error: error || "" });
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`); const tabEl = this.tabList.querySelector(
`[data-session-id="${sessionId}"]`,
);
if (!tabEl) return; if (!tabEl) return;
const dot = tabEl.querySelector('.tab-status'); const dot = tabEl.querySelector(".tab-status");
if (dot) { if (dot) {
dot.className = `tab-status status-${state}`; dot.className = `tab-status status-${state}`;
dot.title = stateLabel(state); dot.title = stateLabel(state);
} }
if (state === 'error' && error) { if (state === "error" && error) {
this.app.notifications.error(`${this._tabLabel(sessionId)}: ${error}`); this.app.notifications.error(`${this._tabLabel(sessionId)}: ${error}`);
} }
} }
@@ -297,7 +328,7 @@ class TabManager {
* @param {string} sessionId * @param {string} sessionId
*/ */
handleSessionClosed(sessionId) { handleSessionClosed(sessionId) {
this.updateSessionState(sessionId, 'disconnected'); this.updateSessionState(sessionId, "disconnected");
} }
/** /**
@@ -311,42 +342,55 @@ class TabManager {
const session = this.app.state.getSession(sessionId); const session = this.app.state.getSession(sessionId);
if (!session) return; if (!session) return;
const menu = createElement('div', 'context-menu'); const menu = createElement("div", "context-menu");
menu.style.left = `${x}px`; menu.style.left = `${x}px`;
menu.style.top = `${y}px`; menu.style.top = `${y}px`;
const isPinned = session.pinned; const isPinned = session.pinned;
const items = [ const items = [
{ label: 'Rename…', action: () => this._promptRename(sessionId) }, { label: "Rename…", action: () => this._promptRename(sessionId) },
{ label: 'Duplicate', action: () => this.duplicateTab(sessionId) }, { label: "Duplicate", action: () => this.duplicateTab(sessionId) },
{ divider: true },
{ label: isPinned ? 'Unpin' : 'Pin', action: () => isPinned ? this.unpinTab(sessionId) : this.pinTab(sessionId) },
{ label: 'Move Left', action: () => this.moveLeft(sessionId) },
{ label: 'Move Right', action: () => this.moveRight(sessionId) },
{ divider: true }, { divider: true },
{ {
label: 'Reconnect', label: isPinned ? "Unpin" : "Pin",
action: () =>
isPinned ? this.unpinTab(sessionId) : this.pinTab(sessionId),
},
{ label: "Move Left", action: () => this.moveLeft(sessionId) },
{ label: "Move Right", action: () => this.moveRight(sessionId) },
{ divider: true },
{
label: "Reconnect",
action: () => this._reconnect(sessionId), action: () => this._reconnect(sessionId),
disabled: session.state === 'connected' || session.state === 'connecting', disabled:
session.state === "connected" || session.state === "connecting",
}, },
{ divider: true }, { divider: true },
{ label: 'Close', className: 'context-menu-danger', action: () => this.closeTab(sessionId) }, {
label: "Close",
className: "context-menu-danger",
action: () => this.closeTab(sessionId),
},
]; ];
for (const entry of items) { for (const entry of items) {
if (entry.divider) { if (entry.divider) {
menu.appendChild(createElement('div', 'context-menu-divider')); menu.appendChild(createElement("div", "context-menu-divider"));
continue; continue;
} }
const btn = createElement('button', `context-menu-item ${entry.className || ''}`, { const btn = createElement(
"button",
`context-menu-item ${entry.className || ""}`,
{
textContent: entry.label, textContent: entry.label,
}); },
);
if (entry.disabled) { if (entry.disabled) {
btn.disabled = true; btn.disabled = true;
btn.classList.add('context-menu-disabled'); btn.classList.add("context-menu-disabled");
} }
btn.addEventListener('click', () => { btn.addEventListener("click", () => {
dismissContextMenus(); dismissContextMenus();
entry.action(); entry.action();
}); });
@@ -357,8 +401,10 @@ class TabManager {
// Keep within viewport // Keep within viewport
const rect = menu.getBoundingClientRect(); const rect = menu.getBoundingClientRect();
if (rect.right > window.innerWidth) menu.style.left = `${window.innerWidth - rect.width - 8}px`; if (rect.right > window.innerWidth)
if (rect.bottom > window.innerHeight) menu.style.top = `${window.innerHeight - rect.height - 8}px`; menu.style.left = `${window.innerWidth - rect.width - 8}px`;
if (rect.bottom > window.innerHeight)
menu.style.top = `${window.innerHeight - rect.height - 8}px`;
} }
/** /**
@@ -397,43 +443,54 @@ class TabManager {
* @private * @private
*/ */
_renderTab(sessionInfo) { _renderTab(sessionInfo) {
const tab = createElement('div', 'tab', { const tab = createElement("div", "tab", {
dataset: { sessionId: sessionInfo.id }, dataset: { sessionId: sessionInfo.id },
draggable: 'true', draggable: "true",
}); });
// Protocol badge // Protocol badge
const protoLabel = sessionInfo.protocol === 'github' ? 'GitHub' : sessionInfo.protocol.toUpperCase(); const protoLabel =
const badge = createElement('span', `tab-badge badge-${sessionInfo.protocol}`, { sessionInfo.protocol === "github"
? "GitHub"
: sessionInfo.protocol.toUpperCase();
const badge = createElement(
"span",
`tab-badge badge-${sessionInfo.protocol}`,
{
textContent: protoLabel, textContent: protoLabel,
}); },
);
tab.appendChild(badge); tab.appendChild(badge);
// Name // Name
const name = createElement('span', 'tab-name', { const name = createElement("span", "tab-name", {
textContent: sessionInfo.tabName || sessionInfo.protocol.toUpperCase(), textContent: sessionInfo.tabName || sessionInfo.protocol.toUpperCase(),
}); });
tab.appendChild(name); tab.appendChild(name);
// Status dot // Status dot
const status = createElement('span', `tab-status status-${sessionInfo.state || 'connecting'}`, { const status = createElement(
"span",
`tab-status status-${sessionInfo.state || "connecting"}`,
{
title: stateLabel(sessionInfo.state), title: stateLabel(sessionInfo.state),
}); },
);
tab.appendChild(status); tab.appendChild(status);
// Pin indicator // Pin indicator
if (sessionInfo.pinned) { if (sessionInfo.pinned) {
tab.classList.add('tab-pinned'); tab.classList.add("tab-pinned");
} }
// SFTP toggle button (SSH sessions only) // SFTP toggle button (SSH sessions only)
if (sessionInfo.protocol === 'ssh') { if (sessionInfo.protocol === "ssh") {
const sftpBtn = createElement('button', 'tab-sftp', { const sftpBtn = createElement("button", "tab-sftp", {
textContent: '📁', textContent: "📁",
title: 'Toggle SFTP file browser', title: "Toggle SFTP file browser",
'aria-label': 'Toggle SFTP', "aria-label": "Toggle SFTP",
}); });
sftpBtn.addEventListener('click', (e) => { sftpBtn.addEventListener("click", (e) => {
e.stopPropagation(); e.stopPropagation();
this.app.sftp.toggle(sessionInfo.id); this.app.sftp.toggle(sessionInfo.id);
}); });
@@ -441,21 +498,21 @@ class TabManager {
} }
// Close button // Close button
const closeBtn = createElement('button', 'tab-close', { const closeBtn = createElement("button", "tab-close", {
textContent: '×', textContent: "×",
'aria-label': 'Close tab', "aria-label": "Close tab",
}); });
closeBtn.addEventListener('click', (e) => { closeBtn.addEventListener("click", (e) => {
e.stopPropagation(); e.stopPropagation();
this.closeTab(sessionInfo.id); this.closeTab(sessionInfo.id);
}); });
tab.appendChild(closeBtn); tab.appendChild(closeBtn);
// Click → activate // Click → activate
tab.addEventListener('click', () => this.setActive(sessionInfo.id)); tab.addEventListener("click", () => this.setActive(sessionInfo.id));
// Right-click → context menu // Right-click → context menu
tab.addEventListener('contextmenu', (e) => { tab.addEventListener("contextmenu", (e) => {
e.preventDefault(); e.preventDefault();
this.showContextMenu(sessionInfo.id, e.clientX, e.clientY); this.showContextMenu(sessionInfo.id, e.clientX, e.clientY);
}); });
@@ -494,9 +551,11 @@ class TabManager {
*/ */
_updateTabStyles(sessionId) { _updateTabStyles(sessionId) {
const session = this.app.state.getSession(sessionId); const session = this.app.state.getSession(sessionId);
const tabEl = this.tabList.querySelector(`[data-session-id="${sessionId}"]`); const tabEl = this.tabList.querySelector(
`[data-session-id="${sessionId}"]`,
);
if (tabEl && session) { if (tabEl && session) {
tabEl.classList.toggle('tab-pinned', !!session.pinned); tabEl.classList.toggle("tab-pinned", !!session.pinned);
} }
} }
@@ -505,9 +564,9 @@ class TabManager {
* @private * @private
*/ */
_updateEmptyState() { _updateEmptyState() {
const empty = document.getElementById('empty-state'); const empty = document.getElementById("empty-state");
if (empty) { if (empty) {
empty.style.display = this.order.length === 0 ? 'flex' : 'none'; empty.style.display = this.order.length === 0 ? "flex" : "none";
} }
} }
@@ -518,9 +577,9 @@ class TabManager {
*/ */
_promptRename(sessionId) { _promptRename(sessionId) {
const session = this.app.state.getSession(sessionId); const session = this.app.state.getSession(sessionId);
const current = session?.tabName || ''; const current = session?.tabName || "";
// Use the app's custom prompt modal (window.prompt is unavailable in WKWebView) // Use the app's custom prompt modal (window.prompt is unavailable in WKWebView)
this.app._inputPrompt('Rename Tab', 'Tab name', current).then((name) => { this.app._inputPrompt("Rename Tab", "Tab name", current).then((name) => {
if (name !== null && name.trim()) { if (name !== null && name.trim()) {
this.renameTab(sessionId, name.trim()); this.renameTab(sessionId, name.trim());
} }
@@ -554,7 +613,7 @@ class TabManager {
*/ */
_tabLabel(sessionId) { _tabLabel(sessionId) {
const session = this.app.state.getSession(sessionId); const session = this.app.state.getSession(sessionId);
return session?.tabName || 'Session'; return session?.tabName || "Session";
} }
// ── Drag and drop ──────────────────────────────────────────────── // ── Drag and drop ────────────────────────────────────────────────
@@ -568,40 +627,40 @@ class TabManager {
let draggedId = null; let draggedId = null;
this.tabList.addEventListener('dragstart', (e) => { this.tabList.addEventListener("dragstart", (e) => {
const tab = e.target.closest('.tab'); const tab = e.target.closest(".tab");
if (!tab) return; if (!tab) return;
draggedId = tab.dataset.sessionId; draggedId = tab.dataset.sessionId;
tab.classList.add('tab-dragging'); tab.classList.add("tab-dragging");
e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.effectAllowed = "move";
}); });
this.tabList.addEventListener('dragover', (e) => { this.tabList.addEventListener("dragover", (e) => {
e.preventDefault(); e.preventDefault();
e.dataTransfer.dropEffect = 'move'; e.dataTransfer.dropEffect = "move";
const target = e.target.closest('.tab'); const target = e.target.closest(".tab");
if (!target || target.dataset.sessionId === draggedId) return; if (!target || target.dataset.sessionId === draggedId) return;
const rect = target.getBoundingClientRect(); const rect = target.getBoundingClientRect();
const midX = rect.left + rect.width / 2; const midX = rect.left + rect.width / 2;
target.classList.toggle('tab-drop-before', e.clientX < midX); target.classList.toggle("tab-drop-before", e.clientX < midX);
target.classList.toggle('tab-drop-after', e.clientX >= midX); target.classList.toggle("tab-drop-after", e.clientX >= midX);
}); });
this.tabList.addEventListener('dragleave', (e) => { this.tabList.addEventListener("dragleave", (e) => {
const target = e.target.closest('.tab'); const target = e.target.closest(".tab");
if (target) { if (target) {
target.classList.remove('tab-drop-before', 'tab-drop-after'); target.classList.remove("tab-drop-before", "tab-drop-after");
} }
}); });
this.tabList.addEventListener('drop', (e) => { this.tabList.addEventListener("drop", (e) => {
e.preventDefault(); e.preventDefault();
const target = e.target.closest('.tab'); const target = e.target.closest(".tab");
if (!target || !draggedId) return; if (!target || !draggedId) return;
target.classList.remove('tab-drop-before', 'tab-drop-after'); target.classList.remove("tab-drop-before", "tab-drop-after");
const targetId = target.dataset.sessionId; const targetId = target.dataset.sessionId;
if (targetId === draggedId) return; if (targetId === draggedId) return;
@@ -616,9 +675,13 @@ class TabManager {
this._reorderDOM(); this._reorderDOM();
}); });
this.tabList.addEventListener('dragend', () => { this.tabList.addEventListener("dragend", () => {
this.tabList.querySelectorAll('.tab').forEach((t) => { this.tabList.querySelectorAll(".tab").forEach((t) => {
t.classList.remove('tab-dragging', 'tab-drop-before', 'tab-drop-after'); t.classList.remove(
"tab-dragging",
"tab-drop-before",
"tab-drop-after",
);
}); });
draggedId = null; draggedId = null;
}); });
@@ -627,5 +690,4 @@ class TabManager {
window.NexusTerminal = window.NexusTerminal || {}; window.NexusTerminal = window.NexusTerminal || {};
window.NexusTerminal.TabManager = TabManager; window.NexusTerminal.TabManager = TabManager;
})(); })();