Phase 2: Split pane terminals, custom themes, and password strength meter

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
This commit is contained in:
2026-04-16 00:00:00 +01:00
parent 5a998aefb9
commit da6320c04b
12 changed files with 1257 additions and 28 deletions
+35 -2
View File
@@ -4,7 +4,7 @@
* Initialises the application shell, loads profiles, and sets up event listeners.
*/
const { State, Sidebar, TabManager, TerminalManager, ProfileEditor, RawViewer, Notifications, Settings, CommandPalette, SecretPrompt, stateLabel } = window.NexusTerminal;
const { State, Sidebar, TabManager, TerminalManager, ProfileEditor, RawViewer, Notifications, Settings, CommandPalette, SecretPrompt, SplitPaneManager, stateLabel } = window.NexusTerminal;
class App {
constructor() {
@@ -18,6 +18,7 @@ class App {
this.settings = new Settings(this);
this.commandPalette = new CommandPalette(this);
this.secretPrompt = new SecretPrompt(this);
this.splitPanes = new SplitPaneManager(this);
}
/**
@@ -321,6 +322,20 @@ class App {
if (e.key === 'Escape') {
this.commandPalette.close();
}
// Ctrl/Cmd+Shift+D — split pane vertically (left/right)
if (mod && e.shiftKey && e.key === 'D') {
e.preventDefault();
this.splitPane('vertical');
return;
}
// Ctrl/Cmd+Shift+E — split pane horizontally (top/bottom)
if (mod && e.shiftKey && e.key === 'E') {
e.preventDefault();
this.splitPane('horizontal');
return;
}
});
}
@@ -345,7 +360,25 @@ class App {
// Window resize → re-fit active terminal (debounced)
const { debounce } = window.NexusTerminal;
window.addEventListener('resize', debounce(() => this.terminals.fitActive(), 100));
window.addEventListener('resize', debounce(() => {
this.terminals.fitActive();
this.splitPanes.refitAll();
}, 100));
}
// ── Split pane ─────────────────────────────────────────────────
/**
* Splits the active session panel in the given direction.
* @param {'horizontal'|'vertical'} direction
*/
splitPane(direction) {
if (!this.state.activeSessionId) return;
if (direction === 'horizontal') {
this.splitPanes.splitHorizontal();
} else {
this.splitPanes.splitVertical();
}
}
// ── Connection lifecycle ─────────────────────────────────────────