feat(terminal): add Terminal panel to Node Details with WebSocket
- Add 'Terminal' button to drawer header
- Implement TerminalPanel (bottom-up fade-in, ~1/3 viewport, left of drawer)
- Connect to ws(s)://{nodeIp}/ws; display incoming messages; input sends raw
- Wire Drawer to pass node IP and close terminal on drawer close
- Add styles/z-index and include script in index.html
This commit is contained in:
@@ -29,16 +29,19 @@ class DrawerComponent {
|
||||
this.detailsDrawer = document.createElement('div');
|
||||
this.detailsDrawer.className = 'details-drawer';
|
||||
|
||||
// Header with close button
|
||||
// Header with actions and close button
|
||||
const header = document.createElement('div');
|
||||
header.className = 'details-drawer-header';
|
||||
header.innerHTML = `
|
||||
<div class="drawer-title">Node Details</div>
|
||||
<button class="drawer-close" aria-label="Close">
|
||||
<div class="drawer-actions">
|
||||
<button class="drawer-terminal-btn" title="Open Terminal" aria-label="Open Terminal">Terminal</button>
|
||||
<button class="drawer-close" aria-label="Close">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M18 6L6 18M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
this.detailsDrawer.appendChild(header);
|
||||
|
||||
@@ -47,11 +50,28 @@ class DrawerComponent {
|
||||
this.detailsDrawerContent.className = 'details-drawer-content';
|
||||
this.detailsDrawer.appendChild(this.detailsDrawerContent);
|
||||
|
||||
// Terminal panel container (positioned left of details drawer)
|
||||
this.terminalPanelContainer = document.createElement('div');
|
||||
this.terminalPanelContainer.className = 'terminal-panel-container';
|
||||
document.body.appendChild(this.terminalPanelContainer);
|
||||
|
||||
document.body.appendChild(this.detailsDrawer);
|
||||
|
||||
// Close handlers
|
||||
const close = () => this.closeDrawer();
|
||||
header.querySelector('.drawer-close').addEventListener('click', close);
|
||||
const terminalBtn = header.querySelector('.drawer-terminal-btn');
|
||||
if (terminalBtn) {
|
||||
terminalBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
const nodeIp = this.activeDrawerComponent && this.activeDrawerComponent.viewModel && this.activeDrawerComponent.viewModel.get('nodeIp');
|
||||
window.TerminalPanel && window.TerminalPanel.open(this.terminalPanelContainer, nodeIp);
|
||||
} catch (err) {
|
||||
console.error('Failed to open terminal:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.detailsDrawerBackdrop.addEventListener('click', close);
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') close();
|
||||
@@ -104,6 +124,9 @@ class DrawerComponent {
|
||||
if (this.detailsDrawer) this.detailsDrawer.classList.remove('open');
|
||||
if (this.detailsDrawerBackdrop) this.detailsDrawerBackdrop.classList.remove('visible');
|
||||
|
||||
// Also close terminal panel if open
|
||||
try { if (window.TerminalPanel) window.TerminalPanel.close(); } catch (_) {}
|
||||
|
||||
// Call close callback if provided
|
||||
if (this.onCloseCallback) {
|
||||
this.onCloseCallback();
|
||||
|
||||
Reference in New Issue
Block a user