diff --git a/.gitignore b/.gitignore index 680461b..e9eff4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,60 @@ -# Build output -build/ +# ========================================================================== +# Nexus Terminal — .gitignore +# ========================================================================== + +# ── Build output ────────────────────────────────────────────────────────── +build/bin/ +build/darwin/ +build/windows/ +build/linux/ dist/ -# Dependencies +# ── Go ──────────────────────────────────────────────────────────────────── vendor/ -frontend/node_modules/ +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out +__debug_bin* +coverage.out +coverage.html -# IDE +# ── Frontend ────────────────────────────────────────────────────────────── +frontend/node_modules/ +frontend/src/js/lib/ + +# ── Wails auto-generated bindings (recreated on every build) ────────────── +frontend/wailsjs/ + +# ── IDE / editor ────────────────────────────────────────────────────────── .idea/ .vscode/ *.swp *.swo *~ +*.sublime-project +*.sublime-workspace +.project +.classpath +.settings/ -# OS +# ── OS metadata ─────────────────────────────────────────────────────────── .DS_Store +.DS_Store? +._* Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ -# Test -coverage.out -coverage.html - -# Binaries -*.exe -*.dll -*.so -*.dylib - -# Debug -__debug_bin* - -# Application data (never commit) +# ── Application data (never commit user data or secrets) ────────────────── *.db *.sqlite +*.sqlite3 vault.json +*.pem +*.key +!build/appicon.png diff --git a/build/appicon.png b/build/appicon.png new file mode 100644 index 0000000..63617fe Binary files /dev/null and b/build/appicon.png differ diff --git a/frontend/src/css/sidebar.css b/frontend/src/css/sidebar.css index 2fe10b2..df3831e 100644 --- a/frontend/src/css/sidebar.css +++ b/frontend/src/css/sidebar.css @@ -77,6 +77,7 @@ letter-spacing: 0.06em; cursor: pointer; transition: color var(--transition-fast), background var(--transition-fast); + -webkit-user-select: none; user-select: none; text-align: left; background: none; diff --git a/frontend/src/css/terminal.css b/frontend/src/css/terminal.css index c911c33..383ddcc 100644 --- a/frontend/src/css/terminal.css +++ b/frontend/src/css/terminal.css @@ -69,7 +69,9 @@ /* xterm.js overrides for consistent look */ .terminal-container .xterm, .session-panel .xterm { + flex: 1; height: 100%; + min-height: 0; padding: var(--space-1); } diff --git a/frontend/src/js/sidebar.js b/frontend/src/js/sidebar.js index eb0185a..83f65ff 100644 --- a/frontend/src/js/sidebar.js +++ b/frontend/src/js/sidebar.js @@ -113,16 +113,16 @@ class Sidebar { // Section header const header = createElement('div', 'sidebar-section-header'); header.innerHTML = ` - ${isCollapsed ? '▸' : '▾'} - ${escapeHtml(title)} - ${profiles.length} + ${isCollapsed ? '▸' : '▾'} + ${escapeHtml(title)} + ${profiles.length} `; header.addEventListener('click', () => this.toggleSection(key)); section.appendChild(header); // Items if (!isCollapsed) { - const list = createElement('div', 'sidebar-section-list'); + const list = createElement('div', 'sidebar-list'); for (const profile of profiles) { list.appendChild(this.renderProfileItem(profile)); } diff --git a/frontend/src/js/terminal.js b/frontend/src/js/terminal.js index 1f65e04..f859de1 100644 --- a/frontend/src/js/terminal.js +++ b/frontend/src/js/terminal.js @@ -67,14 +67,23 @@ class TerminalManager { const container = document.getElementById(containerId); term.open(container); - // Fit the terminal once the container has had time to lay out. - // A single requestAnimationFrame is not enough — the panel may - // still be transitioning from display:none, so we retry briefly. + // Fit the terminal once the container has real dimensions. + // The panel may have just transitioned from display:none, so we + // poll until the container reports a non-trivial size. const doFit = () => { try { fitAddon.fit(); } catch (_) { /* noop */ } }; - requestAnimationFrame(doFit); - setTimeout(doFit, 50); - setTimeout(doFit, 150); - setTimeout(doFit, 400); + let fitAttempts = 0; + const tryFit = () => { + fitAttempts++; + const rect = container.getBoundingClientRect(); + if (rect.width > 10 && rect.height > 10) { + doFit(); + } else if (fitAttempts < 20) { + // Container still has no size — retry + setTimeout(tryFit, 50); + return; + } + }; + requestAnimationFrame(tryFit); // Forward user keystrokes to the backend term.onData((data) => {