From dd62458515e2c4d999a4a5a58f3f27144042e86a Mon Sep 17 00:00:00 2001 From: Calmcacil Date: Mon, 12 Jan 2026 23:23:48 +0100 Subject: [PATCH] Add text selection and copy capability to terminal UI --- .beads/issues.jsonl | 12 ++++---- README.md | 17 +++++++++++ internal/tui/components/search.go | 3 +- internal/tui/screens/detail.go | 4 ++- internal/tui/screens/help.go | 8 ++++- internal/tui/theme/theme.go | 49 ++++++++++++++++++++++++++----- internal/wireguard/status.go | 33 +++++++++++++++++++-- 7 files changed, 106 insertions(+), 20 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index dbce460..595063d 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -3,14 +3,14 @@ {"id":"wg-admin-11o","title":"Implement backup operations","description":"Create backup functionality that saves WireGuard configs, client configs, and metadata to /etc/wg-admin/backups/ with timestamp. Set proper permissions (600 for sensitive files).","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.288606376+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T18:06:07.405162631+01:00","closed_at":"2026-01-12T18:06:07.405162631+01:00","close_reason":"Backup operations implemented in internal/backup/backup.go. CreateBackup with timestamp, retention policy (last 10). ListBackups, RestoreBackup with validation. ReloadWireGuard after restore. Integration with client operations.","dependencies":[{"issue_id":"wg-admin-11o","depends_on_id":"wg-admin-wf1","type":"blocks","created_at":"2026-01-12T17:04:36.19397874+01:00","created_by":"Calmcacil"}]} {"id":"wg-admin-1b0","title":"Add loading spinners for async operations (client creation, restore, QR generation)","description":"Add visual loading feedback using bubbles/spinner for long-running operations like client creation, backup restore, and QR code generation. Users cannot tell if application is working or frozen during these operations.","status":"closed","priority":0,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:39:36.103626542+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:19:04.643165298+01:00","closed_at":"2026-01-12T23:19:04.643165298+01:00","close_reason":"Added loading spinners for async operations (client creation, restore, QR generation). Users can now see visual feedback during long-running operations."} {"id":"wg-admin-1b9","title":"Update documentation for refactored scripts","description":"Update README.md and all documentation to reflect new architecture. Document: wg-install.sh usage (interactive prompts, WGI_ env vars), wg-client-manager commands (add, remove, list, show, qr), environment variable reference, security hardening features, backup/restore procedures. Update examples with new patterns.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:33:43.749727154+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:13:12.828613341+01:00","closed_at":"2026-01-12T17:13:12.828613341+01:00","close_reason":"Documentation updated on main branch. README.md reflects new wg-install.sh and wg-client-manager scripts, WGI_ environment variables, and all usage patterns.","dependencies":[{"issue_id":"wg-admin-1b9","depends_on_id":"wg-admin-slj","type":"blocks","created_at":"2026-01-12T16:33:56.00899014+01:00","created_by":"Calmcacil"}]} -{"id":"wg-admin-2oj","title":"Add screen transition animations for more polished UX","description":"Add brief fade or slide animations when switching screens for more polished feel. Current screen transitions are instant without feedback. Consider lipgloss positioning and tick-based transitions.","status":"open","priority":3,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:48.821352971+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T21:40:48.821352971+01:00"} +{"id":"wg-admin-2oj","title":"Add screen transition animations for more polished UX","description":"Add brief fade or slide animations when switching screens for more polished feel. Current screen transitions are instant without feedback. Consider lipgloss positioning and tick-based transitions.","status":"in_progress","priority":3,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:48.821352971+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:22:39.407954602+01:00"} {"id":"wg-admin-2pl","title":"Improve nftables firewall configuration","description":"Enhance firewall rules based on best practices: add TCP MSS clamping for MTU issues, add connection tracking bypass (notrack) for WireGuard traffic, implement proper rate limiting, ensure ICMPv6 neighbor discovery is allowed, validate rules before applying with nft check.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.15783619+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:37:11.050440729+01:00","closed_at":"2026-01-12T16:37:11.050440729+01:00","close_reason":"Improved nftables firewall configuration with TCP MSS clamping (1360), connection tracking bypass (notrack) for WireGuard UDP traffic, rate limiting for SSH (3/min) and WireGuard (10/s), ensured ICMPv6 neighbor discovery (including nd-router-* messages), and added nft check validation before applying rules."} -{"id":"wg-admin-33z","title":"Standardize TUI formatting and styling across all windows and popups","description":"# Problem\n\nThe TUI application lacks consistent formatting and styling across all screens and components. Each screen defines its own styles independently, leading to:\n\n1. **Inconsistent color usage** - Different screens use different color codes for similar elements\n2. **Duplicated style definitions** - Common styles (titles, help text, borders) are redefined in each file\n3. **Inconsistent modal dimensions** - Hardcoded widths/heights vary across screens\n4. **Visual inconsistency** - Users experience different UI patterns across screens\n5. **Maintenance difficulty** - Changes require updating multiple files\n\n# Current Issues Found\n\n## Color Inconsistencies\n- **Titles**: Mostly Color(62), but variations exist\n- **Help text**: Color(241), Color(243), Color(63) used interchangeably\n- **Status colors**: Different approaches to styling connected/disconnected states\n- **Success messages**: Color(46) used, but not universally\n- **Error messages**: Color(196) used, but styling varies\n\n## Duplicated Style Definitions\nEach screen file has its own style variables:\n- detailTitleStyle, listTitleStyle, addTitleStyle, restoreTitleStyle, etc.\n- Similar styles for help, error, success messages\n- Border styles repeated across multiple files\n\n## Modal Inconsistencies\n- Confirm modal uses 80x24 dimensions\n- Delete confirm modal uses 80x24 dimensions\n- No centralized modal size configuration\n- Different border styles (RoundedBorder vs NormalBorder)\n\n## Table Styles\n- Table styles defined in both list.go and restore.go\n- Same header/selected styles duplicated\n- Should use shared table styling function\n\n# Proposed Solution\n\n## 1. Create Centralized Styles Package\n\nCreate internal/tui/styles/styles.go with:\n\n### Color Palette (Single Source of Truth)\nPrimary colors:\n- ColorPrimary (62) - Titles, primary actions\n- ColorSecondary (241) - Secondary text, labels\n- ColorAccent (57) - Selected items, highlights\n- ColorSuccess (46) - Success messages\n- ColorError (196) - Error messages\n- ColorWarning (226) - Warning messages\n- ColorInfo (243) - Help text, info\n- ColorKey (63) - Keyboard shortcuts\n- ColorBorder (240) - Borders\n- ColorBackground (235) - Modal backgrounds\n\n### Common Style Functions\nCreate reusable style functions:\n- Title() - For all screen titles\n- Help() - For help text and instructions\n- Error() - For error messages\n- Success() - For success messages\n- Warning() - For warning messages\n- Key() - For keyboard shortcuts\n- Modal(width, height) - Consistent modal styling\n- TableHeader() - Table headers\n- TableSelected() - Selected table rows\n- BorderRounded() - Rounded borders\n- BorderNormal() - Normal borders\n\n### Modal Dimensions\nDefine constants:\n- ModalWidth = 80\n- ModalHeight = 24\n\n## 2. Refactor Each Screen to Use Centralized Styles\n\n### Files to Update\n- internal/tui/screens/list.go - Use shared table styles\n- internal/tui/screens/detail.go - Replace local styles with package imports\n- internal/tui/screens/add.go - Use shared title/help styles\n- internal/tui/screens/help.go - Use shared key/label styles\n- internal/tui/screens/qr.go - Use shared title/help/error styles\n- internal/tui/screens/restore.go - Use shared table/modal styles\n\n### Components to Update\n- internal/tui/components/confirm.go - Use shared modal styles\n- internal/tui/components/delete-confirm.go - Use shared modal styles\n- internal/tui/components/search.go - Use shared color palette\n\n## 3. Implementation Approach\n\n### Phase 1: Create Styles Package\n- Create internal/tui/styles/ directory\n- Implement styles.go with color palette and common functions\n- Add table.go for table-specific styles\n\n### Phase 2: Update Components First\n- Update confirm.go and delete-confirm.go to use shared modal styles\n- Update search.go to use shared color palette\n- Test components work correctly\n\n### Phase 3: Update Screens\n- Refactor each screen to import and use styles package\n- Replace local style variables with calls to styles package\n- Test each screen maintains functionality\n\n### Phase 4: Verification\n- Test navigation across all screens\n- Verify consistent visual appearance\n- Check modal sizes and positioning\n- Verify color usage is consistent\n\n# Success Criteria\n\n1. All screens use consistent color palette from single source\n2. No duplicated style definitions across files\n3. Modals have consistent dimensions and styling\n4. Table styling is shared across list and restore screens\n5. Help text, errors, success messages use consistent styles\n6. All keyboard shortcuts use same styling\n7. Code is more maintainable (style changes in one place)\n\n# Files to Create\n- internal/tui/styles/styles.go\n- internal/tui/styles/table.go (optional, could be in styles.go)\n\n# Files to Modify\n- internal/tui/screens/list.go\n- internal/tui/screens/detail.go\n- internal/tui/screens/add.go\n- internal/tui/screens/help.go\n- internal/tui/screens/qr.go\n- internal/tui/screens/restore.go\n- internal/tui/components/confirm.go\n- internal/tui/components/delete-confirm.go\n- internal/tui/components/search.go\n\n# Dependencies\nNone - this is a refactoring task that improves code organization without adding new functionality.","status":"open","priority":1,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T22:19:57.96243735+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T22:21:53.810905089+01:00"} +{"id":"wg-admin-33z","title":"Standardize TUI formatting and styling across all windows and popups","description":"# Problem\n\nThe TUI application lacks consistent formatting and styling across all screens and components. Each screen defines its own styles independently, leading to:\n\n1. **Inconsistent color usage** - Different screens use different color codes for similar elements\n2. **Duplicated style definitions** - Common styles (titles, help text, borders) are redefined in each file\n3. **Inconsistent modal dimensions** - Hardcoded widths/heights vary across screens\n4. **Visual inconsistency** - Users experience different UI patterns across screens\n5. **Maintenance difficulty** - Changes require updating multiple files\n\n# Current Issues Found\n\n## Color Inconsistencies\n- **Titles**: Mostly Color(62), but variations exist\n- **Help text**: Color(241), Color(243), Color(63) used interchangeably\n- **Status colors**: Different approaches to styling connected/disconnected states\n- **Success messages**: Color(46) used, but not universally\n- **Error messages**: Color(196) used, but styling varies\n\n## Duplicated Style Definitions\nEach screen file has its own style variables:\n- detailTitleStyle, listTitleStyle, addTitleStyle, restoreTitleStyle, etc.\n- Similar styles for help, error, success messages\n- Border styles repeated across multiple files\n\n## Modal Inconsistencies\n- Confirm modal uses 80x24 dimensions\n- Delete confirm modal uses 80x24 dimensions\n- No centralized modal size configuration\n- Different border styles (RoundedBorder vs NormalBorder)\n\n## Table Styles\n- Table styles defined in both list.go and restore.go\n- Same header/selected styles duplicated\n- Should use shared table styling function\n\n# Proposed Solution\n\n## 1. Create Centralized Styles Package\n\nCreate internal/tui/styles/styles.go with:\n\n### Color Palette (Single Source of Truth)\nPrimary colors:\n- ColorPrimary (62) - Titles, primary actions\n- ColorSecondary (241) - Secondary text, labels\n- ColorAccent (57) - Selected items, highlights\n- ColorSuccess (46) - Success messages\n- ColorError (196) - Error messages\n- ColorWarning (226) - Warning messages\n- ColorInfo (243) - Help text, info\n- ColorKey (63) - Keyboard shortcuts\n- ColorBorder (240) - Borders\n- ColorBackground (235) - Modal backgrounds\n\n### Common Style Functions\nCreate reusable style functions:\n- Title() - For all screen titles\n- Help() - For help text and instructions\n- Error() - For error messages\n- Success() - For success messages\n- Warning() - For warning messages\n- Key() - For keyboard shortcuts\n- Modal(width, height) - Consistent modal styling\n- TableHeader() - Table headers\n- TableSelected() - Selected table rows\n- BorderRounded() - Rounded borders\n- BorderNormal() - Normal borders\n\n### Modal Dimensions\nDefine constants:\n- ModalWidth = 80\n- ModalHeight = 24\n\n## 2. Refactor Each Screen to Use Centralized Styles\n\n### Files to Update\n- internal/tui/screens/list.go - Use shared table styles\n- internal/tui/screens/detail.go - Replace local styles with package imports\n- internal/tui/screens/add.go - Use shared title/help styles\n- internal/tui/screens/help.go - Use shared key/label styles\n- internal/tui/screens/qr.go - Use shared title/help/error styles\n- internal/tui/screens/restore.go - Use shared table/modal styles\n\n### Components to Update\n- internal/tui/components/confirm.go - Use shared modal styles\n- internal/tui/components/delete-confirm.go - Use shared modal styles\n- internal/tui/components/search.go - Use shared color palette\n\n## 3. Implementation Approach\n\n### Phase 1: Create Styles Package\n- Create internal/tui/styles/ directory\n- Implement styles.go with color palette and common functions\n- Add table.go for table-specific styles\n\n### Phase 2: Update Components First\n- Update confirm.go and delete-confirm.go to use shared modal styles\n- Update search.go to use shared color palette\n- Test components work correctly\n\n### Phase 3: Update Screens\n- Refactor each screen to import and use styles package\n- Replace local style variables with calls to styles package\n- Test each screen maintains functionality\n\n### Phase 4: Verification\n- Test navigation across all screens\n- Verify consistent visual appearance\n- Check modal sizes and positioning\n- Verify color usage is consistent\n\n# Success Criteria\n\n1. All screens use consistent color palette from single source\n2. No duplicated style definitions across files\n3. Modals have consistent dimensions and styling\n4. Table styling is shared across list and restore screens\n5. Help text, errors, success messages use consistent styles\n6. All keyboard shortcuts use same styling\n7. Code is more maintainable (style changes in one place)\n\n# Files to Create\n- internal/tui/styles/styles.go\n- internal/tui/styles/table.go (optional, could be in styles.go)\n\n# Files to Modify\n- internal/tui/screens/list.go\n- internal/tui/screens/detail.go\n- internal/tui/screens/add.go\n- internal/tui/screens/help.go\n- internal/tui/screens/qr.go\n- internal/tui/screens/restore.go\n- internal/tui/components/confirm.go\n- internal/tui/components/delete-confirm.go\n- internal/tui/components/search.go\n\n# Dependencies\nNone - this is a refactoring task that improves code organization without adding new functionality.","status":"in_progress","priority":1,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T22:19:57.96243735+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:22:49.581885604+01:00"} {"id":"wg-admin-37o","title":"Add security hardening","description":"Implement: client name sanitization with regex, pre-shared key (PSK) support option, proper temporary key cleanup with trap handlers, atomic config file operations (write to temp then mv), chmod 0600 for all key files, verify no hardcoded secrets in generated files.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.148392501+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:44:11.582485544+01:00","closed_at":"2026-01-12T16:44:11.582485544+01:00","close_reason":"Implemented all security hardening features: client name sanitization with regex (validate_client_name function), pre-shared key (PSK) support with --psk option, proper temporary key cleanup with trap handlers (cleanup_handler), atomic config file operations (mktemp + mv), chmod 0600 for all key files, and verified no hardcoded secrets (keys generated dynamically or read from files)"} {"id":"wg-admin-3d4","title":"Implement configuration loading system","description":"Implement configuration system to load /etc/wg-admin/config.conf using native Go or Viper library. Support environment variable overrides. Validate required config (SERVER_DOMAIN, WG_PORT, VPN_IPV4_RANGE, VPN_IPV6_RANGE, DNS_SERVERS). Provide clear error messages for missing config.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:02:57.198865993+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:21:51.863786437+01:00","closed_at":"2026-01-12T17:21:51.863786437+01:00","close_reason":"Configuration system implemented in internal/config/config.go. Loads from /etc/wg-admin/config.conf, supports environment variable overrides with WGI_ prefix, validates required fields (SERVER_DOMAIN, WG_PORT, CIDR formats). Provides helper methods for network extraction.","dependencies":[{"issue_id":"wg-admin-3d4","depends_on_id":"wg-admin-4ji","type":"blocks","created_at":"2026-01-12T17:04:44.279588181+01:00","created_by":"Calmcacil"}]} {"id":"wg-admin-4fb","title":"Set up basic TUI skeleton with Bubble Tea","description":"Create main TUI application entry point implementing Bubble Tea's Model-Update-View pattern. Set up root check and logging. Create empty screen types (list, add, detail, qr, help). Implement basic keyboard navigation (q=quit). Add status bar with version and help shortcut (?).","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:02:57.195332445+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:29:25.376578103+01:00","closed_at":"2026-01-12T17:29:25.376578103+01:00","close_reason":"TUI skeleton implemented with Model-Update-View pattern. Main entry point in cmd/wg-tui/main.go with root check, configuration loading integration, basic keyboard navigation (q quit), status bar with version and help. Creates clean separation between TUI model (internal/tui) and main program. Successfully builds.","dependencies":[{"issue_id":"wg-admin-4fb","depends_on_id":"wg-admin-4ji","type":"blocks","created_at":"2026-01-12T17:04:26.666043249+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-4fb","depends_on_id":"wg-admin-3d4","type":"blocks","created_at":"2026-01-12T17:04:26.672887205+01:00","created_by":"Calmcacil"}]} {"id":"wg-admin-4ji","title":"Initialize Go module and project structure","description":"Initialize Go project with go mod init. Create directory structure following plan: cmd/, internal/config, internal/wireguard, internal/tui (screens, components, theme), internal/validation, internal/backup. Add README with project setup instructions.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:02:57.197740013+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:20:34.471816058+01:00","closed_at":"2026-01-12T17:20:34.471816058+01:00","close_reason":"Go module initialized, directory structure created (cmd/, internal/ subdirectories), dependencies added (bubbletea, lipgloss, bubbles, huh, qrterminal), basic TUI skeleton with Model-Update-View pattern implemented. Root check added. Builds successfully.","dependencies":[{"issue_id":"wg-admin-4ji","depends_on_id":"wg-admin-gp4","type":"blocks","created_at":"2026-01-12T17:04:26.670875524+01:00","created_by":"Calmcacil"}]} -{"id":"wg-admin-55x","title":"Add connection quality indicators (Excellent/Good/Fair/Poor) based on handshake time","description":"Currently only shows Connected/Disconnected. Add connection quality indicators based on handshake recency: Excellent (\u003c30s), Good (\u003c2m), Fair (\u003c5m), Poor (\u003e5m). This provides more informative feedback about connection health.","status":"open","priority":2,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:23.26708921+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T21:40:23.26708921+01:00"} +{"id":"wg-admin-55x","title":"Add connection quality indicators (Excellent/Good/Fair/Poor) based on handshake time","description":"Currently only shows Connected/Disconnected. Add connection quality indicators based on handshake recency: Excellent (\u003c30s), Good (\u003c2m), Fair (\u003c5m), Poor (\u003e5m). This provides more informative feedback about connection health.","status":"in_progress","priority":2,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:23.26708921+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:22:39.376880004+01:00"} {"id":"wg-admin-69b","title":"Implement WireGuard client parsing","description":"Parse WireGuard client configuration files from /etc/wireguard/conf.d/client-*.conf. Extract client name, IPv4, IPv6, public key, and PSK status. Create Client struct. Handle file read errors and malformed configs. Validate config syntax.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:02:57.199808074+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:39:44.913242962+01:00","closed_at":"2026-01-12T17:39:44.913242962+01:00","close_reason":"Client struct created in internal/wireguard/client.go with fields: Name, IPv4, IPv6, PublicKey, HasPSK, ConfigPath. ParseClientConfig() parses [Peer] sections from config files. ListClients() scans /etc/wireguard/conf.d/ for client-*.conf files. Handles errors gracefully.","dependencies":[{"issue_id":"wg-admin-69b","depends_on_id":"wg-admin-4fb","type":"blocks","created_at":"2026-01-12T17:04:44.265421971+01:00","created_by":"Calmcacil"}]} {"id":"wg-admin-7ra","title":"Fix help screen documentation - incorrect key binding for viewing details","description":"Help screen shows '[D]' key for viewing client details, but actual access is via 'Enter' key. Update help documentation to show correct keybinding.","status":"closed","priority":0,"issue_type":"bug","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:39:36.098716455+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:00:16.297251855+01:00","closed_at":"2026-01-12T23:00:16.297251855+01:00","close_reason":"Fixed help screen to remove incorrect 'D' key for client details. Enter key is already documented in Navigation section as 'Select'."} {"id":"wg-admin-abw","title":"Create wg-client-manager script","description":"Create new wg-client-manager script for client operations: add, remove, list, show, qr. Implement proper command parsing, use interactive 'read' with 'WGI_' environment variable overrides, call validation functions, use atomic config updates.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.150007325+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:48:38.86400169+01:00","closed_at":"2026-01-12T16:48:38.86400169+01:00","close_reason":"Created wg-client-manager script with all required commands (add, remove, list, show, qr). Implements interactive prompts with WGI_ environment variable overrides, uses validation functions, and performs atomic config updates.","dependencies":[{"issue_id":"wg-admin-abw","depends_on_id":"wg-admin-cwb","type":"blocks","created_at":"2026-01-12T16:28:20.280054863+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-abw","depends_on_id":"wg-admin-37o","type":"blocks","created_at":"2026-01-12T16:28:20.299310073+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-abw","depends_on_id":"wg-admin-lzl","type":"blocks","created_at":"2026-01-12T16:28:20.300924186+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-abw","depends_on_id":"wg-admin-wsk","type":"blocks","created_at":"2026-01-12T16:28:20.354270061+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-abw","depends_on_id":"wg-admin-0va","type":"blocks","created_at":"2026-01-12T16:28:21.926811217+01:00","created_by":"Calmcacil"}]} @@ -22,7 +22,7 @@ {"id":"wg-admin-ddl","title":"Debug connectivity status showing Disconnected incorrectly","description":"Debug why connectivity status shows 'Disconnected' when client is actually connected. Investigate the finalizePeerStatus logic and the 3-minute threshold for connection status.","status":"closed","priority":1,"issue_type":"bug","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:14:49.564914056+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:17:12.292551145+01:00","closed_at":"2026-01-12T19:17:12.292551145+01:00","close_reason":"Fixed q key handling to only quit from list screen, increased connection timeout threshold to 5 minutes"} {"id":"wg-admin-dho","title":"Fix 'q' key to go back to list instead of quitting app","description":"Fix key handling in detail screen so 'q' returns to list instead of quitting the entire application. Currently pressing 'q' in detail view closes the app entirely.","status":"closed","priority":1,"issue_type":"bug","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:14:49.562771127+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:17:12.284907523+01:00","closed_at":"2026-01-12T19:17:12.284907523+01:00","close_reason":"Fixed q key handling to only quit from list screen, increased connection timeout threshold to 5 minutes"} {"id":"wg-admin-e07","title":"Implement Dracula color scheme theme","description":"Add Dracula color scheme to the theme registry. Use the official Dracula color palette: Background #282A36, Primary (purple) #BD93F9, Success (green) #50FA7B, Warning (orange) #FFB86C, Error (red) #FF5555, Muted #6272A4.","status":"closed","priority":1,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:07:40.305419584+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:08:50.621690085+01:00","closed_at":"2026-01-12T19:08:50.621690085+01:00","close_reason":"Implemented Dracula and Everforest color schemes and set Everforest as default theme"} -{"id":"wg-admin-ee9","title":"Add text selection and copy capability to terminal UI","description":"\n# Add Text Selection and Copy Capability to Terminal UI\n\n## Problem\n\nUsers need to copy text from TUI application, but text selection and copying functionality is limited or non-existent. Users cannot control which text gets selected when dragging over it with mouse.\n\n## Current State\n\n### Terminal Selection Behavior\n- **Mouse selection is terminal-controlled**: The terminal emulator, not the application, controls text selection\n- **No programmatic control**: Applications can't specify which text is selectable\n- **Selection mode varies**: Different terminals have different selection behavior (word, line, block, etc.)\n- **Copy varies**: Copy mechanism depends on terminal/OS combination\n\n### Current Application Support\n- **No clipboard integration**: The app has no clipboard access library\n- **Removed clipboard feature**: We replaced \"Copy Public Key\" with config display modal\n- **Scrollable modals**: Config display and other modals are scrollable but text can still be selected\n\n## Terminal Limitations (Research Results)\n\n### What We CAN'T Control\n\n**Based on Bubble Tea Issue #162** (tracked since Nov 2021):\n1. **Selection boundaries**: Can't specify which text is selectable\n2. **Selection mode**: Can't force word/line/block selection\n3. **Copy action**: Can't programmatically trigger copy\n4. **Selection persistence**: Can't control how long selection persists\n5. **Multi-selection**: Can't control multiple selection regions\n\n### What We CAN Control\n\n1. **Text content**: We control what text is displayed\n2. **Visual highlighting**: We can highlight specific text regions\n3. **Copy buttons**: We can add \"copy\" functionality (requires clipboard API)\n4. **Alternative outputs**: We can export/save content in other ways\n\n### The Mouse Mode Tradeoff\n\nBubble Tea applications have a **binary limitation**:\n\n| Mouse Mode | Native Selection | App Mouse Events |\n|-------------|-------------------|-------------------|\n| OFF (default) | ✓ Works | ✗ None |\n| WithMouseAllMotion | ✗ Disabled | ✓ Full control |\n\n**Evidence**: When using `WithMouseAllMotion()`, native terminal text selection is completely disabled. There is no middle ground.\n\n## Industry Standard Solution: SHIFT Key Workaround\n\n### How Other TUIs Handle This\n\n| Application | Approach | Documentation |\n|-------------|-----------|----------------|\n| **GDB TUI** | Hold SHIFT while dragging | GDB docs: \"Hold SHIFT to access terminal's native mouse copy/paste\" |\n| **tmux** | Hold SHIFT to bypass tmux mouse | tmux docs: Standard practice |\n| **ROCGDB TUI** | Hold SHIFT to access terminal | Same as GDB |\n| **lazygit** | Documents SHIFT requirement | Users request native selection (issue #5034) |\n\n**This is the universal workaround** for TUI applications.\n\n### How It Works\n\n1. User holds **SHIFT key** on keyboard\n2. User drags mouse to select text\n3. Terminal treats this as a **native mouse event** (bypasses TUI)\n4. Terminal emulator's native selection works normally\n5. User copies with terminal-specific shortcut (Ctrl+Shift+C, Cmd+C, etc.)\n\n### Platform-Specific Copy Shortcuts\n\n| Platform | Shortcut | Notes |\n|----------|----------|-------|\n| Linux/WSL | Ctrl+Shift+C | Most terminal emulators |\n| macOS | Cmd+C | Terminal.app |\n| Windows | Click right | Terminal selection context menu |\n| SSH | Depends on client terminal | Not controlled by app |\n\n## Recommended Implementation\n\n### Phase 1: Document SHIFT Workaround (Immediate)\n\n**Add to application help and README:**\n\n```\nText Selection\n--------------\nTo select text for copying from modals or lists:\n• Hold SHIFT key while dragging mouse with left button\n• This bypasses TUI mouse handling and enables terminal's native text selection\n• Then use your terminal's copy shortcut:\n • Linux/WSL: Ctrl+Shift+C (or terminal-specific shortcut)\n • macOS: Cmd+C\n • Windows: Click right (or use terminal copy)\n```\n\n**Benefits:**\n- Works on all terminals (standard behavior)\n- No code changes needed\n- Full native selection control (word, line, block modes)\n- Zero performance impact\n\n### Phase 2: Add Explicit Copy Button (Enhancement)\n\nAdd copy buttons in modals that use clipboard API:\n\n**For Config Display Modal:**\n- Add \"[C] Copy Full Config\" action\n- Copy entire config to system clipboard\n- Works over SSH with X11 forwarding if available\n- Fallback to SHIFT selection if clipboard unavailable\n\n**Implementation approach:**\n```go\n// Add clipboard library\nimport \"github.com/charmbracelet/x/exp/clipboard\"\n\n// In ConfigDisplayModel Update()\ncase \"c\", \"C\":\n // Copy to clipboard with error handling\n if err := clipboard.WriteAll(m.config); err != nil {\n return m, func() tea.Msg {\n return errMsg{err: fmt.Errorf(\"clipboard not available: %w\", err)}\n }\n }\n m.clipboardCopied = true\n return m, nil\n```\n\n**Platform compatibility:**\n- ✓ macOS - Native clipboard access\n- ✓ Linux (native) - X11/Wayland required\n- ✓ WSL - Windows clipboard access\n- ✗ SSH (plain) - X11 forwarding required (ssh -X)\n- ✓ SSH (-X flag) - Uses local clipboard\n\n### Phase 3: Add Export Feature (Optional Enhancement)\n\nAdd ability to export configuration to file:\n\n```go\ncase \"s\", \"S\":\n return m, func() tea.Msg {\n err := os.WriteFile(s.configPath, m.config, 0644)\n if err != nil {\n return errMsg{err: err}\n }\n return ConfigSavedMsg{Path: s.configPath}\n }\n```\n\n## Implementation Details\n\n### Step 1: Document SHIFT Workaround\n\n**Files to update:**\n1. **README.md** - Add text selection section\n2. **Help screen** (internal/tui/screens/help.go) - Add SHIFT key documentation\n3. **Config display modal help** - Add \"Hold SHIFT to select\" note\n\n**Example help text:**\n```\nText Selection \u0026 Copying\n-------------------------\n• Hold SHIFT + drag mouse to select text (native selection)\n• Then use terminal copy shortcut (Ctrl+Shift+C / Cmd+C)\n• Or press [C] to copy to clipboard (when available)\n```\n\n### Step 2: Add Clipboard Library (Optional)\n\n**If implementing explicit copy:**\n\n1. **Add dependency** to go.mod:\n ```\n go get github.com/charmbracelet/x/exp/clipboard\n ```\n\n2. **Update ConfigDisplay component** (internal/tui/components/config-display.go):\n - Import clipboard library\n - Add clipboardCopied field\n - Handle 'C' key to copy\n - Show success/error feedback\n - Add to help text\n\n3. **Handle clipboard errors** - Gracefully degrade when clipboard unavailable\n\n## Search Box Note (Separate Issue)\n\n**Regarding the user's comment about search:**\n\nThe search functionality on the list screen should work independently. It's activated with '/' key and filters the client list. When config modal is open in the detail screen, the search in the list screen is not relevant (different screen).\n\nSearch functionality investigation is covered by issue **wg-admin-v7g** (Fix search box functionality on main client list screen).\n\n## Files to Modify\n\n**Documentation (Required):**\n1. **README.md** - Add text selection instructions\n2. **internal/tui/screens/help.go** - Add SHIFT key to help screen\n\n**Implementation (Optional - if adding explicit copy):**\n3. **internal/tui/components/config-display.go** - Add clipboard copy functionality\n4. **internal/tui/screens/detail.go** - Handle clipboard copied messages\n5. **go.mod** - Add clipboard dependency\n\n## Success Criteria\n\n1. SHIFT key workaround documented in README and help\n2. Users understand how to select text for copying\n3. (Optional) Explicit copy button works when clipboard API available\n4. (Optional) Graceful degradation when clipboard unavailable\n\n## Technical Notes\n\n**Bubble Tea Mouse Mode Tradeoff:**\n- `WithMouseAllMotion()` enables app mouse events but disables native selection\n- There is no partial mouse mode\n- DEC terminal modes are terminal-wide, not per-application\n- This is a fundamental terminal limitation, not Bubble Tea specific\n\n**Industry Standard:**\n- SHIFT key bypass is documented by GDB, tmux, and standard TUI applications\n- Works universally across terminals\n- Zero code changes required for basic functionality\n\n## Dependencies\n\n**For SHIFT workaround:** None (documentation only)\n\n**For explicit copy (optional):**\n- `github.com/charmbracelet/x/exp/clipboard` - Cross-platform clipboard\n\n## Priority\n\n**MEDIUM (P2)** - SHIFT workaround is industry standard and works immediately; explicit copy is enhancement but has platform limitations (SSH)\n","status":"open","priority":2,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T22:38:54.083153165+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T22:51:23.419482581+01:00"} +{"id":"wg-admin-ee9","title":"Add text selection and copy capability to terminal UI","description":"\n# Add Text Selection and Copy Capability to Terminal UI\n\n## Problem\n\nUsers need to copy text from TUI application, but text selection and copying functionality is limited or non-existent. Users cannot control which text gets selected when dragging over it with mouse.\n\n## Current State\n\n### Terminal Selection Behavior\n- **Mouse selection is terminal-controlled**: The terminal emulator, not the application, controls text selection\n- **No programmatic control**: Applications can't specify which text is selectable\n- **Selection mode varies**: Different terminals have different selection behavior (word, line, block, etc.)\n- **Copy varies**: Copy mechanism depends on terminal/OS combination\n\n### Current Application Support\n- **No clipboard integration**: The app has no clipboard access library\n- **Removed clipboard feature**: We replaced \"Copy Public Key\" with config display modal\n- **Scrollable modals**: Config display and other modals are scrollable but text can still be selected\n\n## Terminal Limitations (Research Results)\n\n### What We CAN'T Control\n\n**Based on Bubble Tea Issue #162** (tracked since Nov 2021):\n1. **Selection boundaries**: Can't specify which text is selectable\n2. **Selection mode**: Can't force word/line/block selection\n3. **Copy action**: Can't programmatically trigger copy\n4. **Selection persistence**: Can't control how long selection persists\n5. **Multi-selection**: Can't control multiple selection regions\n\n### What We CAN Control\n\n1. **Text content**: We control what text is displayed\n2. **Visual highlighting**: We can highlight specific text regions\n3. **Copy buttons**: We can add \"copy\" functionality (requires clipboard API)\n4. **Alternative outputs**: We can export/save content in other ways\n\n### The Mouse Mode Tradeoff\n\nBubble Tea applications have a **binary limitation**:\n\n| Mouse Mode | Native Selection | App Mouse Events |\n|-------------|-------------------|-------------------|\n| OFF (default) | ✓ Works | ✗ None |\n| WithMouseAllMotion | ✗ Disabled | ✓ Full control |\n\n**Evidence**: When using `WithMouseAllMotion()`, native terminal text selection is completely disabled. There is no middle ground.\n\n## Industry Standard Solution: SHIFT Key Workaround\n\n### How Other TUIs Handle This\n\n| Application | Approach | Documentation |\n|-------------|-----------|----------------|\n| **GDB TUI** | Hold SHIFT while dragging | GDB docs: \"Hold SHIFT to access terminal's native mouse copy/paste\" |\n| **tmux** | Hold SHIFT to bypass tmux mouse | tmux docs: Standard practice |\n| **ROCGDB TUI** | Hold SHIFT to access terminal | Same as GDB |\n| **lazygit** | Documents SHIFT requirement | Users request native selection (issue #5034) |\n\n**This is the universal workaround** for TUI applications.\n\n### How It Works\n\n1. User holds **SHIFT key** on keyboard\n2. User drags mouse to select text\n3. Terminal treats this as a **native mouse event** (bypasses TUI)\n4. Terminal emulator's native selection works normally\n5. User copies with terminal-specific shortcut (Ctrl+Shift+C, Cmd+C, etc.)\n\n### Platform-Specific Copy Shortcuts\n\n| Platform | Shortcut | Notes |\n|----------|----------|-------|\n| Linux/WSL | Ctrl+Shift+C | Most terminal emulators |\n| macOS | Cmd+C | Terminal.app |\n| Windows | Click right | Terminal selection context menu |\n| SSH | Depends on client terminal | Not controlled by app |\n\n## Recommended Implementation\n\n### Phase 1: Document SHIFT Workaround (Immediate)\n\n**Add to application help and README:**\n\n```\nText Selection\n--------------\nTo select text for copying from modals or lists:\n• Hold SHIFT key while dragging mouse with left button\n• This bypasses TUI mouse handling and enables terminal's native text selection\n• Then use your terminal's copy shortcut:\n • Linux/WSL: Ctrl+Shift+C (or terminal-specific shortcut)\n • macOS: Cmd+C\n • Windows: Click right (or use terminal copy)\n```\n\n**Benefits:**\n- Works on all terminals (standard behavior)\n- No code changes needed\n- Full native selection control (word, line, block modes)\n- Zero performance impact\n\n### Phase 2: Add Explicit Copy Button (Enhancement)\n\nAdd copy buttons in modals that use clipboard API:\n\n**For Config Display Modal:**\n- Add \"[C] Copy Full Config\" action\n- Copy entire config to system clipboard\n- Works over SSH with X11 forwarding if available\n- Fallback to SHIFT selection if clipboard unavailable\n\n**Implementation approach:**\n```go\n// Add clipboard library\nimport \"github.com/charmbracelet/x/exp/clipboard\"\n\n// In ConfigDisplayModel Update()\ncase \"c\", \"C\":\n // Copy to clipboard with error handling\n if err := clipboard.WriteAll(m.config); err != nil {\n return m, func() tea.Msg {\n return errMsg{err: fmt.Errorf(\"clipboard not available: %w\", err)}\n }\n }\n m.clipboardCopied = true\n return m, nil\n```\n\n**Platform compatibility:**\n- ✓ macOS - Native clipboard access\n- ✓ Linux (native) - X11/Wayland required\n- ✓ WSL - Windows clipboard access\n- ✗ SSH (plain) - X11 forwarding required (ssh -X)\n- ✓ SSH (-X flag) - Uses local clipboard\n\n### Phase 3: Add Export Feature (Optional Enhancement)\n\nAdd ability to export configuration to file:\n\n```go\ncase \"s\", \"S\":\n return m, func() tea.Msg {\n err := os.WriteFile(s.configPath, m.config, 0644)\n if err != nil {\n return errMsg{err: err}\n }\n return ConfigSavedMsg{Path: s.configPath}\n }\n```\n\n## Implementation Details\n\n### Step 1: Document SHIFT Workaround\n\n**Files to update:**\n1. **README.md** - Add text selection section\n2. **Help screen** (internal/tui/screens/help.go) - Add SHIFT key documentation\n3. **Config display modal help** - Add \"Hold SHIFT to select\" note\n\n**Example help text:**\n```\nText Selection \u0026 Copying\n-------------------------\n• Hold SHIFT + drag mouse to select text (native selection)\n• Then use terminal copy shortcut (Ctrl+Shift+C / Cmd+C)\n• Or press [C] to copy to clipboard (when available)\n```\n\n### Step 2: Add Clipboard Library (Optional)\n\n**If implementing explicit copy:**\n\n1. **Add dependency** to go.mod:\n ```\n go get github.com/charmbracelet/x/exp/clipboard\n ```\n\n2. **Update ConfigDisplay component** (internal/tui/components/config-display.go):\n - Import clipboard library\n - Add clipboardCopied field\n - Handle 'C' key to copy\n - Show success/error feedback\n - Add to help text\n\n3. **Handle clipboard errors** - Gracefully degrade when clipboard unavailable\n\n## Search Box Note (Separate Issue)\n\n**Regarding the user's comment about search:**\n\nThe search functionality on the list screen should work independently. It's activated with '/' key and filters the client list. When config modal is open in the detail screen, the search in the list screen is not relevant (different screen).\n\nSearch functionality investigation is covered by issue **wg-admin-v7g** (Fix search box functionality on main client list screen).\n\n## Files to Modify\n\n**Documentation (Required):**\n1. **README.md** - Add text selection instructions\n2. **internal/tui/screens/help.go** - Add SHIFT key to help screen\n\n**Implementation (Optional - if adding explicit copy):**\n3. **internal/tui/components/config-display.go** - Add clipboard copy functionality\n4. **internal/tui/screens/detail.go** - Handle clipboard copied messages\n5. **go.mod** - Add clipboard dependency\n\n## Success Criteria\n\n1. SHIFT key workaround documented in README and help\n2. Users understand how to select text for copying\n3. (Optional) Explicit copy button works when clipboard API available\n4. (Optional) Graceful degradation when clipboard unavailable\n\n## Technical Notes\n\n**Bubble Tea Mouse Mode Tradeoff:**\n- `WithMouseAllMotion()` enables app mouse events but disables native selection\n- There is no partial mouse mode\n- DEC terminal modes are terminal-wide, not per-application\n- This is a fundamental terminal limitation, not Bubble Tea specific\n\n**Industry Standard:**\n- SHIFT key bypass is documented by GDB, tmux, and standard TUI applications\n- Works universally across terminals\n- Zero code changes required for basic functionality\n\n## Dependencies\n\n**For SHIFT workaround:** None (documentation only)\n\n**For explicit copy (optional):**\n- `github.com/charmbracelet/x/exp/clipboard` - Cross-platform clipboard\n\n## Priority\n\n**MEDIUM (P2)** - SHIFT workaround is industry standard and works immediately; explicit copy is enhancement but has platform limitations (SSH)\n","status":"in_progress","priority":2,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T22:38:54.083153165+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:22:39.599383867+01:00"} {"id":"wg-admin-ej7","title":"Implement color themes","description":"Add support for color themes using lipgloss. Create default theme with primary, success, warning, and error colors. Support theme switching through config file or environment variable.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.290145203+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:48:32.299616088+01:00","closed_at":"2026-01-12T17:48:32.299616088+01:00","close_reason":"Color themes implemented with Theme package. Three built-in themes (default, dark, light) with ColorScheme support. THEME config option and environment variable support added to config.go. Lipgloss styles use theme colors.","dependencies":[{"issue_id":"wg-admin-ej7","depends_on_id":"wg-admin-xum","type":"blocks","created_at":"2026-01-12T17:04:53.269323117+01:00","created_by":"Calmcacil"}]} {"id":"wg-admin-gp4","title":"Create Go TUI epic","description":"Epic: Convert wg-client-manager bash script to a modern, responsive Go TUI application using Bubble Tea framework. Provides better UX with interactive forms, real-time status updates, and intuitive keyboard navigation.","status":"closed","priority":1,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.286393088+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T18:06:58.972856627+01:00","closed_at":"2026-01-12T18:06:58.972856627+01:00","close_reason":"All Go TUI tasks completed across 6 phases:\n\nPhase 1 (Foundation): Project init, config system, TUI skeleton\nPhase 2 (Client List): Parsing, table, real-time status \nPhase 3 (Add Client): Form, key generation, config files\nPhase 4 (Detail/Delete): Detail view, delete functionality, QR codes\nPhase 5 (UX): Search, help screen, color themes\nPhase 6 (Backup/Restore): Backup operations, restore functionality\n\nImplementation details:\n- Total packages added: bubbletea, lipgloss, bubbles, huh, qrterminal\n- Files created: 25+ Go source files\n- All features implemented: CRUD clients, status checking, QR codes, themes, search, backup/restore\n- Build successful with ~6MB binary\n- All tasks closed and synced to remote\n\nApplication ready for testing and deployment."} {"id":"wg-admin-gw9","title":"Add search and filter clients","description":"Implement client search functionality with keyboard shortcut (/). Allow filtering by client name, IP address, or status. Highlight matching results in real-time as user types.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.285733479+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T18:06:07.246176563+01:00","closed_at":"2026-01-12T18:06:07.246176563+01:00","close_reason":"Search component implemented in internal/tui/components/search.go. Real-time filtering by name, IP, or status with '/' activation. Filter types cycle with Tab. Match count display. Integrated with list screen to dynamically update client table.","dependencies":[{"issue_id":"wg-admin-gw9","depends_on_id":"wg-admin-xum","type":"blocks","created_at":"2026-01-12T17:04:36.200521151+01:00","created_by":"Calmcacil"}]} @@ -36,9 +36,9 @@ {"id":"wg-admin-ka8","title":"Generate QR codes for clients","description":"Generate ANSI-colored QR codes from client configs using qrterminal library. Support both inline and fullscreen QR display modes. Handle terminal resize events for optimal QR rendering.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.273562645+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:48:32.013388859+01:00","closed_at":"2026-01-12T17:48:32.013388859+01:00","close_reason":"QR code display implemented using qrterminal. Supports inline and fullscreen modes with f toggle. Handles terminal resize events. Returns to list on q/Esc.","dependencies":[{"issue_id":"wg-admin-ka8","depends_on_id":"wg-admin-wf1","type":"blocks","created_at":"2026-01-12T17:04:36.203581002+01:00","created_by":"Calmcacil"}]} {"id":"wg-admin-kfs","title":"Create configuration file format for WireGuard settings","description":"Design and implement /etc/wg-admin/config file to replace hardcoded values. Include: SERVER_DOMAIN, WG_PORT, VPN_IPV4_RANGE, VPN_IPV6_RANGE, WG_INTERFACE, DNS_SERVERS, and other configurable parameters. Support both file-based and environment variable override.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.148859434+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:31:29.339557739+01:00","closed_at":"2026-01-12T16:31:29.339557739+01:00","close_reason":"Config file approach replaced with interactive prompts using 'read', with 'WGI_' prefixed environment variable overrides. No persistent config file needed."} {"id":"wg-admin-lzl","title":"Add improved error handling and traps","description":"Implement: EXIT trap for cleanup on script interruption, pre-install validation (disk space, port availability, root check), rollback mechanism for failed operations, better error messages with actionable guidance, log all operations with timestamps.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.154445252+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:44:11.575490008+01:00","closed_at":"2026-01-12T16:44:11.575490008+01:00","close_reason":"Implemented all error handling and trap features: EXIT trap for cleanup (cleanup_handler catches EXIT,INT,TERM,HUP), pre-install validation (pre_install_validation checks disk space, port availability, root), rollback mechanism (rollback_installation function with BACKUP_DIR), better error messages with actionable guidance (all errors include specific fix suggestions), and logging with timestamps (log_info, log_error, log_warn functions)"} -{"id":"wg-admin-nyp","title":"Add keyboard shortcut discoverability hints on each screen","description":"Show available keyboard shortcuts on each screen in footer or help overlay. Users currently must remember or check help. Display context-sensitive shortcuts based on current screen and current state (e.g., when search is active).","status":"open","priority":3,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:48.826752641+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T21:40:48.826752641+01:00"} +{"id":"wg-admin-nyp","title":"Add keyboard shortcut discoverability hints on each screen","description":"Show available keyboard shortcuts on each screen in footer or help overlay. Users currently must remember or check help. Display context-sensitive shortcuts based on current screen and current state (e.g., when search is active).","status":"in_progress","priority":3,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:48.826752641+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:22:49.898621171+01:00"} {"id":"wg-admin-o4o","title":"Implement WireGuard key generation","description":"Implement WireGuard key generation using wg genkey and wg pubkey commands. Generate client private key, public key, and optional pre-shared key (PSK). Ensure atomic file writes and proper permissions (0600).","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.283256646+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:51:24.471200438+01:00","closed_at":"2026-01-12T17:51:24.471200438+01:00","close_reason":"Implemented WireGuard key generation in internal/wireguard/keys.go with GeneratePrivateKey(), GeneratePublicKey(), GeneratePSK(), GenerateKeyPair(), StoreKey(), LoadKey(), ValidateKey(), and CleanupTempKeys() functions. Uses wg genkey, wg pubkey, and wg genpsk commands with atomic writes (temp file + mv) and 0600 permissions. Includes key validation (44 base64 chars) and temp key tracking for cleanup. Package builds successfully.","dependencies":[{"issue_id":"wg-admin-o4o","depends_on_id":"wg-admin-wod","type":"blocks","created_at":"2026-01-12T17:04:52.815358118+01:00","created_by":"Calmcacil"}]} -{"id":"wg-admin-onv","title":"Enhance search with match highlighting, count display, Ctrl+U to clear","description":"Improve search component with: highlight matching text in results, show number of matches found, add Ctrl+U shortcut to clear search, Tab to cycle filter types. Better filtering experience with clear exit from search mode.","status":"open","priority":3,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:48.818862183+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T21:40:48.818862183+01:00"} +{"id":"wg-admin-onv","title":"Enhance search with match highlighting, count display, Ctrl+U to clear","description":"Improve search component with: highlight matching text in results, show number of matches found, add Ctrl+U shortcut to clear search, Tab to cycle filter types. Better filtering experience with clear exit from search mode.","status":"in_progress","priority":3,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:48.818862183+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:22:38.765877552+01:00"} {"id":"wg-admin-p3o","title":"Set Everforest as default theme","description":"Change the default theme from 'default' to 'everforest' in the GetTheme function. This will make Everforest the default when no THEME environment variable is set.","status":"closed","priority":1,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:07:40.302533502+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:08:50.637133192+01:00","closed_at":"2026-01-12T19:08:50.637133192+01:00","close_reason":"Implemented Dracula and Everforest color schemes and set Everforest as default theme"} {"id":"wg-admin-p6q","title":"Test theme switching functionality","description":"Test theme switching by setting THEME environment variable to different values (dracula, everforest, default, dark, light) and verify that the TUI renders with correct colors.","status":"closed","priority":1,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:07:40.33610722+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:10:22.227831273+01:00","closed_at":"2026-01-12T19:10:22.227831273+01:00","close_reason":"Documentation updated with theme options, build successful, theme switching logic verified"} {"id":"wg-admin-q0x","title":"Research and document Dracula/Everforest color schemes","description":"Research and document the official color specifications for Dracula and Everforest color schemes. Extract hex values for primary, success, warning, error, and muted colors to use in the TUI theme implementation.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:07:40.305301935+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:08:01.204915996+01:00","closed_at":"2026-01-12T19:08:01.204915996+01:00","close_reason":"Research completed - documented Dracula and Everforest color specifications with hex values for implementation"} diff --git a/README.md b/README.md index 85df6b7..79f3674 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,23 @@ Then run: sudo ~/wireguard.sh load-clients ``` +## Text Selection & Copying + +To copy client configurations or other text from the terminal UI: + +### Text Selection +- Hold **SHIFT key** while dragging your mouse with the left button +- This bypasses TUI mouse handling and enables your terminal's native text selection +- Then use your terminal's copy shortcut: + - **Linux/WSL**: Ctrl+Shift+C (or terminal-specific shortcut) + - **macOS**: Cmd+C + - **Windows**: Click right (or use terminal copy) + +### Copy Buttons (when available) +- Some modals may have explicit copy buttons (e.g., "C" to copy config) +- These work when clipboard API is available (native Linux, macOS, WSL) +- Over SSH requires X11 forwarding (`ssh -X`) for clipboard access + ## Client Setup ### Importing the config diff --git a/internal/tui/components/search.go b/internal/tui/components/search.go index baf908b..608a6c0 100644 --- a/internal/tui/components/search.go +++ b/internal/tui/components/search.go @@ -1,6 +1,7 @@ package components import ( + "strconv" "strings" "github.com/charmbracelet/bubbles/textinput" @@ -263,7 +264,7 @@ func (m *SearchModel) HighlightMatches(value string) string { Bold(true) before := value[:index] - match := value[index+len(query)] + match := value[index : index+len(query)] after := value[index+len(query):] return lipgloss.JoinHorizontal( diff --git a/internal/tui/screens/detail.go b/internal/tui/screens/detail.go index c3c7112..9142e2f 100644 --- a/internal/tui/screens/detail.go +++ b/internal/tui/screens/detail.go @@ -152,7 +152,9 @@ func (s *DetailScreen) renderContent() string { statusText := s.status if s.status == wireguard.StatusConnected { - statusText = theme.StyleSuccess.Bold(true).Render("● " + s.status) + duration := time.Since(s.lastHandshake) + quality := wireguard.CalculateQuality(duration) + statusText = theme.StyleSuccess.Bold(true).Render("● " + s.status + " (" + quality + ")") } else { statusText = theme.StyleError.Bold(true).Render("● " + s.status) } diff --git a/internal/tui/screens/help.go b/internal/tui/screens/help.go index 06b35d3..654dd96 100644 --- a/internal/tui/screens/help.go +++ b/internal/tui/screens/help.go @@ -41,7 +41,6 @@ func (s *HelpScreen) View() string { // Breadcrumb: Help breadcrumb := components.RenderBreadcrumb([]components.BreadcrumbItem{{Label: "Help", ID: "help"}}) - // Styles borderStyle := lipgloss.NewStyle(). BorderStyle(lipgloss.RoundedBorder()). @@ -90,6 +89,11 @@ func (s *HelpScreen) View() string { keyStyle.Render("/") + descStyle.Render("Search") + "\n" + keyStyle.Render("q") + descStyle.Render("Quit") + copyGroup := categoryStyle.Render("Text Selection & Copy") + "\n" + + keyStyle.Render("SHIFT+drag") + descStyle.Render("Select text") + "\n" + + keyStyle.Render("Ctrl+Shift+C") + descStyle.Render("Copy (Linux)") + "\n" + + keyStyle.Render("Cmd+C") + descStyle.Render("Copy (macOS)") + // Two-column layout leftColumn := lipgloss.JoinVertical(lipgloss.Left, navigationGroup, @@ -99,6 +103,8 @@ func (s *HelpScreen) View() string { rightColumn := lipgloss.JoinVertical(lipgloss.Left, otherGroup, + "", + copyGroup, ) content := lipgloss.JoinHorizontal(lipgloss.Top, leftColumn, " ", rightColumn) diff --git a/internal/tui/theme/theme.go b/internal/tui/theme/theme.go index f5bbb16..8183f94 100644 --- a/internal/tui/theme/theme.go +++ b/internal/tui/theme/theme.go @@ -30,14 +30,20 @@ var ( once sync.Once // Global styles that can be used throughout the application - StylePrimary lipgloss.Style - StyleSuccess lipgloss.Style - StyleWarning lipgloss.Style - StyleError lipgloss.Style - StyleMuted lipgloss.Style - StyleTitle lipgloss.Style - StyleSubtitle lipgloss.Style - StyleHelpKey lipgloss.Style + StylePrimary lipgloss.Style + StyleSuccess lipgloss.Style + StyleWarning lipgloss.Style + StyleError lipgloss.Style + StyleMuted lipgloss.Style + StyleTitle lipgloss.Style + StyleSubtitle lipgloss.Style + StyleHelpKey lipgloss.Style + StyleValue lipgloss.Style + StyleDimmed lipgloss.Style + StyleTableHeader lipgloss.Style + StyleTableSelected lipgloss.Style + StyleBorder lipgloss.Color + StyleBackground lipgloss.Color ) // DefaultTheme is the standard blue-based theme @@ -176,6 +182,33 @@ func ApplyTheme(theme *Theme) { StyleHelpKey = lipgloss.NewStyle(). Foreground(theme.Scheme.Primary). Bold(true) + + // Value style for content values + StyleValue = lipgloss.NewStyle(). + Foreground(lipgloss.Color("255")) + + // Dimmed style for overlay content + StyleDimmed = lipgloss.NewStyle(). + Foreground(theme.Scheme.Muted) + + // Table header style + StyleTableHeader = lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("240")). + BorderBottom(true). + Bold(true) + + // Table selected style + StyleTableSelected = lipgloss.NewStyle(). + Foreground(lipgloss.Color("229")). + Background(lipgloss.Color("57")). + Bold(false) + + // Border color + StyleBorder = lipgloss.Color("240") + + // Background color for modals + StyleBackground = lipgloss.Color("235") } // GetThemeNames returns a list of available theme names diff --git a/internal/wireguard/status.go b/internal/wireguard/status.go index 962a2eb..e9880ac 100644 --- a/internal/wireguard/status.go +++ b/internal/wireguard/status.go @@ -15,6 +15,15 @@ const ( StatusConnected = "Connected" // StatusDisconnected indicates a peer is not connected StatusDisconnected = "Disconnected" + + // QualityExcellent indicates handshake was very recent (< 30s) + QualityExcellent = "Excellent" + // QualityGood indicates handshake was recent (< 2m) + QualityGood = "Good" + // QualityFair indicates handshake was acceptable (< 5m) + QualityFair = "Fair" + // QualityPoor indicates handshake was old (> 5m) + QualityPoor = "Poor" ) // PeerStatus represents the status of a WireGuard peer @@ -25,7 +34,8 @@ type PeerStatus struct { LatestHandshake time.Time `json:"latest_handshake"` TransferRx string `json:"transfer_rx"` TransferTx string `json:"transfer_tx"` - Status string `json:"status"` // "Connected" or "Disconnected" + Status string `json:"status"` // "Connected" or "Disconnected" + Quality string `json:"quality,omitempty"` // "Excellent", "Good", "Fair", "Poor" (if connected) } // GetClientStatus checks if a specific client is connected @@ -119,6 +129,20 @@ func parsePeersOutput(output string) []PeerStatus { return peers } +// CalculateQuality returns the connection quality based on handshake time +func CalculateQuality(timeSinceHandshake time.Duration) string { + if timeSinceHandshake < 30*time.Second { + return QualityExcellent + } + if timeSinceHandshake < 2*time.Minute { + return QualityGood + } + if timeSinceHandshake < 5*time.Minute { + return QualityFair + } + return QualityPoor +} + // finalizePeerStatus determines the peer's status based on handshake time func finalizePeerStatus(peer *PeerStatus, handshake string, transfer string) PeerStatus { peer.TransferRx = "" @@ -140,13 +164,16 @@ func finalizePeerStatus(peer *PeerStatus, handshake string, transfer string) Pee } } - // Determine status based on handshake + // Determine status and quality based on handshake if handshake != "" { peer.LatestHandshake = parseHandshake(handshake) + timeSinceHandshake := time.Since(peer.LatestHandshake) + // Peer is considered connected if handshake is recent (within 5 minutes) // This allows for ~12 missed keepalive intervals (at 25 seconds each) - if time.Since(peer.LatestHandshake) < 5*time.Minute { + if timeSinceHandshake < 5*time.Minute { peer.Status = StatusConnected + peer.Quality = calculateQuality(timeSinceHandshake) } else { peer.Status = StatusDisconnected }