feat: minimize terminal
This commit is contained in:
@@ -10,17 +10,22 @@
|
||||
this.connectedIp = null;
|
||||
this.isOpen = false;
|
||||
this.resizeHandler = null;
|
||||
this.isMinimized = false;
|
||||
this.dockEl = null;
|
||||
this.dockBtnEl = null;
|
||||
this.lastNodeIp = null;
|
||||
}
|
||||
|
||||
open(container, nodeIp) {
|
||||
try {
|
||||
this.container = container;
|
||||
if (!this.container) return;
|
||||
|
||||
if (!this.panelEl) {
|
||||
this._buildPanel();
|
||||
}
|
||||
|
||||
this.lastNodeIp = nodeIp || this.lastNodeIp;
|
||||
|
||||
// Reset any leftover inline positioning so CSS centering applies
|
||||
if (this.container) {
|
||||
this.container.style.right = '';
|
||||
@@ -30,16 +35,31 @@
|
||||
this.container.style.width = '';
|
||||
}
|
||||
|
||||
// Show panel with animation
|
||||
requestAnimationFrame(() => {
|
||||
this.panelEl.classList.add('visible');
|
||||
this.isOpen = true;
|
||||
this.inputEl && this.inputEl.focus();
|
||||
});
|
||||
if (!this.isMinimized) {
|
||||
if (this.panelEl) {
|
||||
this.panelEl.classList.remove('minimized');
|
||||
this.panelEl.classList.remove('visible');
|
||||
requestAnimationFrame(() => {
|
||||
this.panelEl.classList.add('visible');
|
||||
this.inputEl && this.inputEl.focus();
|
||||
});
|
||||
}
|
||||
this._hideDock();
|
||||
} else {
|
||||
if (this.panelEl) {
|
||||
this.panelEl.classList.remove('visible');
|
||||
this.panelEl.classList.add('minimized');
|
||||
}
|
||||
this._showDock();
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
// Connect websocket
|
||||
if (nodeIp) {
|
||||
this._connect(nodeIp);
|
||||
} else if (this.lastNodeIp && !this.socket) {
|
||||
this._connect(this.lastNodeIp);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('TerminalPanel.open error:', err);
|
||||
@@ -51,6 +71,9 @@
|
||||
if (!this.isOpen) return;
|
||||
this.panelEl && this.panelEl.classList.remove('visible');
|
||||
this.isOpen = false;
|
||||
this.isMinimized = false;
|
||||
if (this.panelEl) this.panelEl.classList.remove('minimized');
|
||||
this._hideDock();
|
||||
if (this.socket) {
|
||||
try { this.socket.close(); } catch (_) {}
|
||||
this.socket = null;
|
||||
@@ -70,7 +93,7 @@
|
||||
<div class="terminal-header">
|
||||
<div class="terminal-title">Terminal</div>
|
||||
<div class="terminal-actions">
|
||||
<button class="terminal-clear-btn" title="Clear">Clear</button>
|
||||
<button class="terminal-minimize-btn" title="Minimize">_</button>
|
||||
<button class="terminal-close-btn" title="Close">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -79,6 +102,7 @@
|
||||
</div>
|
||||
<div class="terminal-input-row">
|
||||
<input type="text" class="terminal-input" placeholder="Type and press Enter to send" />
|
||||
<button class="terminal-clear-btn" title="Clear">Clear</button>
|
||||
<button class="terminal-send-btn">Send</button>
|
||||
</div>
|
||||
`;
|
||||
@@ -89,6 +113,7 @@
|
||||
const sendBtn = this.panelEl.querySelector('.terminal-send-btn');
|
||||
const closeBtn = this.panelEl.querySelector('.terminal-close-btn');
|
||||
const clearBtn = this.panelEl.querySelector('.terminal-clear-btn');
|
||||
const minimizeBtn = this.panelEl.querySelector('.terminal-minimize-btn');
|
||||
|
||||
const sendHandler = () => {
|
||||
const value = (this.inputEl && this.inputEl.value) || '';
|
||||
@@ -106,6 +131,7 @@
|
||||
});
|
||||
if (closeBtn) closeBtn.addEventListener('click', (e) => { e.stopPropagation(); this.close(); });
|
||||
if (clearBtn) clearBtn.addEventListener('click', (e) => { e.stopPropagation(); this._clear(); });
|
||||
if (minimizeBtn) minimizeBtn.addEventListener('click', (e) => { e.stopPropagation(); this.minimize(); });
|
||||
}
|
||||
|
||||
_connect(nodeIp) {
|
||||
@@ -160,6 +186,74 @@
|
||||
}
|
||||
}
|
||||
|
||||
minimize() {
|
||||
try {
|
||||
if (!this.panelEl || this.isMinimized) return;
|
||||
this.panelEl.classList.remove('visible');
|
||||
this.panelEl.classList.add('minimized');
|
||||
this.isMinimized = true;
|
||||
this.isOpen = true;
|
||||
this._showDock();
|
||||
} catch (err) {
|
||||
console.error('TerminalPanel.minimize error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
restore() {
|
||||
try {
|
||||
if (!this.panelEl || !this.isMinimized) return;
|
||||
this.panelEl.classList.remove('minimized');
|
||||
requestAnimationFrame(() => {
|
||||
this.panelEl.classList.add('visible');
|
||||
this.inputEl && this.inputEl.focus();
|
||||
});
|
||||
this.isMinimized = false;
|
||||
this.isOpen = true;
|
||||
this._hideDock();
|
||||
} catch (err) {
|
||||
console.error('TerminalPanel.restore error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
_ensureDock() {
|
||||
if (this.dockEl) return this.dockEl;
|
||||
const dock = document.createElement('div');
|
||||
dock.className = 'terminal-dock';
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'terminal-dock-btn';
|
||||
button.title = 'Show Terminal';
|
||||
button.setAttribute('aria-label', 'Show Terminal');
|
||||
button.innerHTML = `
|
||||
<span class="terminal-dock-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M4 17l6-6-6-6"></path>
|
||||
<path d="M12 19h8"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="terminal-dock-label">Terminal</span>
|
||||
`;
|
||||
button.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.restore();
|
||||
});
|
||||
dock.appendChild(button);
|
||||
document.body.appendChild(dock);
|
||||
this.dockEl = dock;
|
||||
this.dockBtnEl = button;
|
||||
return dock;
|
||||
}
|
||||
|
||||
_showDock() {
|
||||
const dock = this._ensureDock();
|
||||
if (dock) dock.classList.add('visible');
|
||||
}
|
||||
|
||||
_hideDock() {
|
||||
if (this.dockEl) this.dockEl.classList.remove('visible');
|
||||
}
|
||||
|
||||
_send(text) {
|
||||
try {
|
||||
if (!this.socket || this.socket.readyState !== 1) {
|
||||
|
||||
Reference in New Issue
Block a user