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}
`;
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) => {