- Generated new terminal-themed icon (>_ prompt on dark charcoal)
- macOS: .icns with all retina sizes (16-1024), via iconutil
- Windows: multi-resolution .ico (16-256)
- Linux: individual PNGs (16-512)
- Info.plist: set bundle ID to com.nexusterminal.app, added
LSApplicationCategoryType (developer-tools), disabled automatic
and sudden termination
- wails.json: added Windows icon path
Changed from flex-sibling layout (broken by absolute-positioned terminal
container) to an overlay approach: the SFTP panel is now position:absolute
on the right edge with a drop shadow, sliding over the terminal. The
resize handle is embedded inside the panel at its left edge.
- Added 📁 folder icon button on SSH tabs to toggle the SFTP file browser
- Button appears on hover/active, styled consistently with the close button
- Removed conflicting Cmd+Shift+F shortcut (clashes with macOS find)
- Added platform-specific window options (Mac, Windows, Linux)
- Added F11 fullscreen toggle and Escape to exit fullscreen
- Cleaned up SFTP panel on session close
SFTP file browser:
- Created internal/sftpclient wrapping github.com/pkg/sftp for remote file
operations (list, stat, mkdir, remove, rename, download, upload, pwd)
- Integrated SFTP into session manager with lazy client creation per session
- Added API methods for all SFTP operations with native file dialogs
- Built SFTPBrowser frontend component — right-side resizable panel with
breadcrumb navigation, sorted file listing, context actions (download,
rename, delete), upload, new folder, and custom modal dialogs
- Toggle via Cmd+Shift+F on active SSH sessions
- Auto-refits terminal when panel opens/closes/resizes
- Panel cleaned up automatically when session closes
OpenSSH config import:
- Created internal/sshconfig parser for ~/.ssh/config Host blocks
- Converts to profiles with hostname, port, user, identity file,
ProxyJump (first hop), ForwardAgent, and Compression support
- Skips wildcard-only hosts and Match blocks
- Import button added to Settings > About tab
Automatic update checking:
- Created internal/updater querying GitHub Releases API
- Simple semver comparison, 10-second timeout
- Dev builds never trigger update prompts
- Check button added to Settings > About tab
Session Recording & Playback:
- Add recording package with asciicast v2 format writer and reader
- Recorder captures terminal I/O with timestamps from SSH and Telnet sessions
- Input and output events recorded via hooks in session manager callbacks
- Recording store manages .cast files in ~/Library/Application Support/NexusTerminal/recordings
- Recording browser modal lists all saved recordings with metadata
- Full playback viewer with embedded xterm.js terminal, progress scrubber,
play/pause/restart controls, and 0.5x-10x speed adjustment
- Recording indicator (pulsing red dot) shown on active tab during capture
- Keyboard shortcut Cmd+Shift+R to toggle recording on active session
- Tests for recording, store listing, deletion, and double-close safety
Encrypted Profile Export/Import:
- Add exportimport package with AES-256-GCM encryption (Argon2id key derivation)
- File format: version byte + 32-byte salt + 12-byte nonce + ciphertext
- Same Argon2id parameters as vault (128 MiB, 3 iterations, 4 threads)
- Export modal with profile selection checkboxes and password entry
- Import modal with password entry and conflict strategy (skip/overwrite/duplicate)
- Native save/open file dialogs for .nexus files
- Tests for round-trip encryption, wrong password rejection, corrupt files
- Replaced placeholder import/export with full encrypted implementation
UI:
- Added export and recordings buttons to sidebar footer
- Recording and playback CSS with charcoal design tokens
- Wired RecordingManager and ExportImportUI into app.js
Split Pane Layout:
- Add SplitPaneManager with recursive tree-based pane splitting
- Support horizontal (Cmd+Shift+E) and vertical (Cmd+Shift+D) splits
- Draggable resize dividers with 100px minimum pane size
- Hover-visible close button to unsplit panes
- Session picker in new panes for connecting separate terminals
- Automatic xterm refit on all pane resize operations
Custom Terminal Themes:
- Add themes.js with 8 preset colour schemes: Charcoal (default),
Solarized Dark, Monokai, Nord, Dracula, One Dark, Gruvbox Dark,
Tokyo Night
- Add TerminalTheme preference to AppPreferences model
- Add colour scheme selector to Settings → Terminal tab
- Live-apply theme changes to all active terminals on save
- TerminalManager now uses themed colours instead of hardcoded values
Master Password Strength Meter:
- Add visual strength bar to vault creation dialog
- Entropy estimation based on length, character diversity, uniqueness
- Four levels: Weak (red), Fair (amber), Good (green), Strong (green)
- Animated transitions on strength changes
- Charcoal-themed CSS matching modal design
Argon2id:
- Increase memory parameter from 64 MiB to 128 MiB for stronger KDF
SSH Agent:
- Add agent.go with connectAgent(), agentAuthMethod(), requestAgentForwarding()
- Integrate agent auth into buildAuthMethods() (tried first when enabled)
- Set up agent forwarding after PTY allocation when agentForwarding is true
- Track and clean up agent socket connection on session close
- Add AgentAuth and AgentForwarding fields to SSHConfig model
- Add SSH Agent section to profile editor with toggle controls
Port Forwarding (new):
- Add PortForward model with local/remote direction, bind/dest addresses
- Implement ForwardManager in sshclient/portforward.go with full
bidirectional tunnel support for both local and remote forwarding
- Integrate ForwardManager into SSH client lifecycle (auto-start on
connect, clean shutdown on close)
- Add session manager methods: ListPortForwards, StartPortForward,
StopPortForward with event emission
- Add API surface: ListPortForwards, StartPortForward, StopPortForward
- Add validation for port-forward rules in ValidateSSHConfig
- Add port forwarding section to profile editor with direction, bind/dest
host/port, label, and enable toggle
- Add frontend event handling for forward:started/stopped/error
- Style port-forward rows with charcoal card design matching jump hosts
Jump Host UI improvements:
- Add private key path field to jump host editor rows
- Collect privateKeyPath in form value collection
- Restyle jump host rows as two-line cards with clear field grouping
The SQLite schema had CHECK(protocol IN ('ssh','telnet','raw')) which
rejected GitHub profiles. Added migration 002 that recreates the profiles
table with the 'github' value in the constraint and a github_config column.
Updated all storage queries (CREATE, UPDATE, SELECT) and both scan
functions to include the github_config column.
New protocol type 'github' for browsing repositories via the GitHub REST API.
Creates a non-terminal panel showing repos grouped by owner/org, sorted by
last commit date (newest first), with search filtering and auto-refresh.
Backend:
- ProtocolGitHub constant and GitHubConfig/GitHubRepo model structs
- New internal/githubclient package (REST API, pagination, token validation)
- Session manager connectGitHub() with periodic refresh goroutine
- API surface: RefreshGitHubRepos() for on-demand refresh
- Token stored via existing credential infrastructure (keychain/vault)
Frontend:
- GitHub tab in profile editor with API URL and token storage fields
- New github.js viewer component (grouped repos, badges, language dots)
- github.css with charcoal-themed repo cards and org headers
- Protocol colour tokens, tab badge, sidebar indicator for GitHub
- Event wiring for session:github:repos and session:github:error
- Cleanup on tab close
Supports GitHub Enterprise via configurable API base URL.
Sidebar:
- Profiles are now organised into group-based sections
- Groups appear alphabetically between Favourites/Recent and Ungrouped
- Section key uses 'group:Name' prefix to avoid collisions
- Ungrouped profiles fall into 'Ungrouped' (or 'All Connections' if
no groups exist)
Profile editor:
- Add Group combo-box field to General section
- Uses HTML datalist backed by distinct group names from all profiles
- Typing a new name creates the group; selecting existing reuses it
- Help text explains the behaviour
Save handler:
- folder field now collected from form and trimmed
The Go model already had a 'folder' field — this change wires it
into the UI end-to-end.
Root cause: The PTY is allocated at 80x24 during the SSH handshake.
The frontend xterm.js fits correctly to the container (e.g. 120x35),
but term.onResize fires before the backend connection is ready, so the
ResizeSession call is silently rejected. When the connection completes,
fitAddon.fit() sees the terminal already has the right cols/rows and
does NOT fire another onResize event. The PTY stays at 80x24.
Fix: On 'session:state → connected', explicitly read the current
terminal cols/rows and send them to the backend via ResizeSession,
bypassing the onResize event dependency.
Also fix empty-state ordering in addTab() — hide the empty-state
BEFORE activating the panel so the panel-container gets the full
workspace height immediately (empty-state has flex:1 and was stealing
half the height during initial layout).
Simplify createTerminal() fit logic — force a synchronous reflow
via offsetHeight access, then fit immediately.
Tab close confirmation:
- Add is-visible to both backdrop AND modal element — the modal CSS
starts with visibility:hidden/opacity:0, so the modal was invisible
despite the backdrop blur appearing
Section headers (sidebar + profile editor):
- Add mousedown preventDefault() on all clickable section headers —
WKWebView initiates a text selection gesture on mousedown for divs,
swallowing the first click and requiring a double-click
- Fix sidebar delete profile to use custom modal instead of blocked
window.confirm()
Credential storage:
- buildSecretCallback now respects profile's secretMode field:
- 'saved': checks keychain/vault first, prompts if missing, then
persists to keychain (preferred) or encrypted vault
- 'session-only': caches in memory per-session, prompts once
- 'ask-every-time': always prompts
- Previously the callback always prompted and never saved
- Change default secretMode from 'ask-every-time' to 'saved' in both
Go defaults and JS frontend defaults
Sidebar:
- Add -webkit-user-select: none to section headers (WebKit prefix)
- Fix class mismatches: sidebar-section-chevron → section-chevron,
sidebar-section-count → section-count, sidebar-section-list → sidebar-list
Terminal resize:
- Poll container dimensions after creation — retry fit every 50ms
until container has non-zero width/height (up to 20 attempts)
- Add flex: 1 and min-height: 0 to .xterm inside session-panel
so it participates in flex layout correctly
.gitignore:
- Comprehensive rewrite covering build output, Wails bindings,
frontend libs, IDE files, OS metadata, and application data
Theme:
- Replace all purple/blue-tinted backgrounds (#1a1a2e, #1f1f35, etc.)
with true neutral charcoal greys (#1c1c1c, #232323, #2a2a2a, etc.)
- Neutralise text colours (remove blue tint from secondary/tertiary)
- Update xterm.js theme and session overlay to match
Terminal resize:
- Add fitSession(sessionId) method for targeted terminal fitting
- Fire fit at 30ms, 150ms, and 500ms after 'connected' state event
- Ensures terminal fills container even when layout settles late
- Add pencil edit button on each sidebar profile item (visible on hover)
- Fire fitAddon.fit() multiple times after terminal creation (50/150/400ms)
to catch the panel finishing its CSS layout
- Re-fit terminal when session:state transitions to 'connected'
Layout:
- Add panel-container and session-panel CSS so workspace content
doesn't overlap sidebar
- Session panels use position: absolute to fill workspace area
- Workspace is now flex column for proper empty-state/panel stacking
Terminal:
- xterm.js overrides now target .session-panel in addition to
.terminal-container so terminal fills available space
- Added debounced window resize handler for terminal re-fit
Notifications:
- Rewrote Notifications class to emit .notification-* classes matching
the existing polished CSS (was emitting .toast-* with no CSS)
- Icon is now a small coloured circle badge instead of oversized SVG
Sidebar:
- Fixed class mismatches: sidebar-dot → protocol-indicator,
sidebar-item-sub → sidebar-item-host, sidebar-star → sidebar-item-star
- protocolColour() now returns proto-ssh etc. matching CSS selectors
Other:
- Replaced prompt() in tab rename with custom _inputPrompt modal
- Change ConnectSession/ConnectQuick to return SessionInfo (not string)
- Change UpdateProfile to return (Profile, error) for frontend use
- Make session connection asynchronous to avoid Wails event loop deadlock
- Implement event-based secret prompt (backend ↔ frontend via requestID)
- Add TestConnection API for TCP reachability checks with latency
- Add 'Save & Connect' button in profile editor
- Wire _testConnection to real backend TCP check
- Add btn-accent CSS for the green action button
Add cursor: pointer and -webkit-user-select: none to form section
headers so WebKit does not initiate text selection on the first click,
which was swallowing the toggle interaction.
WebKit's WKWebView rejects top-level const/let declarations that shadow
global function names. Since utils.js defines functions like stateLabel()
at the global scope (non-module scripts), and later files destructure
the same names from window.NexusTerminal using const, WebKit throws:
SyntaxError: Can't create duplicate variable that shadows a global property
Fix: wrap every JS file in an IIFE so all declarations are function-scoped.
Also remove temporary diagnostic instrumentation.
Replace ES module import/export with a global namespace pattern to avoid
silent failures in the Wails v2 WKWebView which does not reliably support
<script type="module"> tags.
- Remove all import/export statements from JS files
- Register each module's exports on window.NexusTerminal
- Destructure dependencies from window.NexusTerminal at file top
- Load all scripts in dependency order in index.html as plain <script> tags
- Moved main.go from cmd/app/ to project root (Wails convention)
- Makefile now resolves wails binary via GOPATH/bin fallback
- Updated wails/v2 dependency from v2.9.2 to v2.12.0
- Fixed preferences round-trip bug (store as single JSON blob)
- Fixed import strategy test (needs non-empty profile data)
- embed.go: embed index.html + all:src directly instead of all:dist,
so `go build` works without a prior npm build step
- index.html: add missing xterm.css vendor stylesheet link
- build.js: add buildDist() to assemble dist/ with index.html and the
full src/ tree; update required-file paths to match dist/ layout
- Add placeholder xterm lib stubs in src/js/lib/ and src/css/lib/ so
the app can start before `npm install` is run
- .gitignore: stop ignoring the lib placeholder files so they are
available on a fresh clone
Add the session manager (internal/sessions/manager.go) that orchestrates
multiple simultaneous SSH, Telnet, and RAW TCP sessions with:
- Protocol-specific connection logic using existing client implementations
- Credential resolution via OS keychain → encrypted vault → user prompt
- Wails runtime event emission for frontend state synchronisation
- Full session lifecycle management (connect, disconnect, reconnect)
- RAW session history and capture support
- Telnet break signal forwarding
Add the Wails-bound API service (internal/api/api.go) exposing:
- Profile CRUD with search, filtering, favourites, and duplication
- Session management (connect, disconnect, resize, write)
- RAW-specific text/hex/escaped write methods
- Secret store initialisation, unlock/lock, and per-profile secrets
- Known host listing and deletion
- Preferences get/save
- Profile import/export with skip/overwrite/duplicate strategies
- Audit log retrieval
- Native file dialog wrappers and app info
Remove conflicting protocol client stubs (client.go) that duplicated
the comprehensive implementations in sshclient.go, telnetclient.go,
and rawclient.go.