* Add documentation in Chinese language (#160)

* Update file naming and structure for mobile support

* Add conditional desktop/mobile rendering

* Mobile terminal

* Fix overwritten i18n (#161)

* Add comprehensive Chinese internationalization support

- Implemented i18n framework with react-i18next for multi-language support
- Added Chinese (zh) and English (en) translation files with comprehensive coverage
- Localized Admin interface, authentication flows, and error messages
- Translated FileManager operations and UI elements
- Updated HomepageAuth component with localized authentication messages
- Localized LeftSidebar navigation and host management
- Added language switcher component (shown after login only)
- Configured default language as English with Chinese as secondary option
- Localized TOTPSetup two-factor authentication interface
- Updated Docker build to include translation files
- Achieved 95%+ UI localization coverage across core components

Co-Authored-By: Claude <noreply@anthropic.com>

* Extend Chinese localization coverage to Host Manager components

- Added comprehensive translations for HostManagerHostViewer component
- Localized all host management UI text including import/export features
- Translated error messages and confirmation dialogs for host operations
- Added translations for HostManagerHostEditor validation messages
- Localized connection details, organization settings, and form labels
- Fixed syntax error in FileManagerOperations component
- Achieved near-complete localization of SSH host management interface
- Updated placeholders and tooltips for better user guidance

Co-Authored-By: Claude <noreply@anthropic.com>

* Complete comprehensive Chinese localization for Termix

- Added full localization support for Tunnel components (connected/disconnected states, retry messages)
- Localized all tunnel status messages and connection errors
- Added translations for port forwarding UI elements
- Verified Server, TopNavbar, and Tab components already have complete i18n support
- Achieved 99%+ localization coverage across entire application
- All core UI components now fully support Chinese and English languages

This completes the comprehensive internationalization effort for the Termix SSH management platform.

Co-Authored-By: Claude <noreply@anthropic.com>

* Localize additional Host Manager components and authentication settings

- Added translations for all authentication options (Password, Key, SSH Private Key)
- Localized form labels in HostManagerHostEditor (Pin Connection, Enable Terminal/Tunnel/FileManager)
- Translated Upload/Update Key button states
- Localized Host Viewer and Add/Edit Host tab labels
- Added Chinese translations for all host management settings
- Fixed duplicate translation keys in JSON files

Co-Authored-By: Claude <noreply@anthropic.com>

* Extend localization coverage to UI components and common strings

- Added comprehensive common translations (online/offline, success/error, etc.)
- Localized status indicator component with all status states
- Updated FileManagerLeftSidebar toast messages for rename/delete operations
- Added translations for UI elements (close, toggle sidebar, etc.)
- Expanded placeholder translations for form inputs
- Added Chinese translations for all new common strings
- Improved consistency across component status messages

Co-Authored-By: Claude <noreply@anthropic.com>

* Complete Chinese localization for remaining UI components

- Add comprehensive Chinese translations for Host Manager component
  - Translate all form labels, buttons, and descriptions
  - Add translations for SSH configuration warnings and instructions
  - Localize tunnel connection settings and port forwarding options

- Localize SSH Tools panel
  - Translate key recording functionality
  - Add translations for settings and configuration options

- Translate homepage welcome messages and navigation elements
  - Add Chinese translations for login success messages
  - Localize "Updates & Releases" section title
  - Translate sidebar "Host Manager" button

- Fix translation key display issues
  - Remove duplicate translation keys in both language files
  - Ensure all components properly reference translation keys
  - Fix hosts.tunnelConnections key mapping

This completes the full Chinese localization of the Termix application,
achieving near 100% UI translation coverage while maintaining English
as the default language.

* Complete final Chinese localization for Host Manager tunnel configuration

- Add Chinese translations for authentication UI elements
  - Translate "Authentication", "Password", and "Key" tab labels
  - Localize SSH private key and key password fields
  - Add translations for key type selector

- Localize tunnel connection configuration descriptions
  - Translate retry attempts and retry interval descriptions
  - Add dynamic tunnel forwarding description with port parameters
  - Localize endpoint SSH configuration labels

- Fix missing translation keys
  - Add "upload" translation for file upload button
  - Ensure all FormLabel and FormDescription elements use translation keys

This completes the comprehensive Chinese localization of the entire
Termix application, achieving 100% UI translation coverage.

* Fix PR feedback: Improve Profile section translations and UX

- Fixed password reset translations in Profile section
- Moved language selector from TopNavbar to Profile page
- Added profile.selectPreferredLanguage translation key
- Improved user experience for language preferences

* Apply critical OIDC and notification system fixes while preserving i18n

- Merge OIDC authentication fixes from 3877e90:
  * Enhanced JWKS discovery mechanism with multiple backup URLs
  * Better support for non-standard OIDC providers (Authentik, etc.)
  * Improved error handling for "Failed to get user information"
- Migrate to unified Sonner toast notification system:
  * Replace custom success/error state management
  * Remove redundant alert state variables
  * Consistent user feedback across all components
- Improve code quality and function naming conventions
- PRESERVE all existing i18n functionality and Chinese translations

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix OIDC errors for "Failed to get user information"

* Fix OIDC errors for "Failed to get user information"

* Fix spelling error

* Migrate everything to alert system, update user.ts for OIDC updates.

* Fix OIDC errors for "Failed to get user information"

* Fix OIDC errors for "Failed to get user information"

* Fix spelling error

* Migrate everything to alert system, update user.ts for OIDC updates.

* Update env

* Fix users.ts and schema for override

* Convert web app to Electron desktop application

- Add Electron main process with developer tools support
- Create preload script for secure context bridge
- Configure electron-builder for packaging
- Update Vite config for Electron compatibility (base: './')
- Add environment variable support for API host configuration
- Fix i18n to use relative paths for Electron file protocol
- Restore multi-port backend architecture (8081-8085)
- Add enhanced backend startup script with port checking
- Update package.json with Electron dependencies and build scripts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Complete Electron desktop application implementation

- Add backend auto-start functionality in main process
- Fix authentication token storage for Electron environment
- Implement localStorage-based token management in Electron
- Add proper Electron environment detection via preload script
- Fix WebSocket connections for terminal functionality
- Resolve font file loading issues in packaged application
- Update API endpoints to work with backend auto-start
- Streamline build scripts with unified electron:package command
- Fix better-sqlite3 native module compatibility issues
- Ensure all services start automatically in production mode

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Remove releases folder from git and force Desktop UI.

* Improve mobile support with half-baked custom keyboard

* Fix API routing

* Upgrade mobile keyboard with more keys.

* Add cross-platform support and clean up obsolete files

- Add electron-packager scripts for Windows, macOS, and Linux
- Include universal architecture support for macOS
- Add electron:package:all for building all platforms
- Remove obsolete start-backend.sh script (replaced by Electron auto-start)
- Improve ignore patterns to exclude repo-images folder
- Add platform-specific icon configurations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix build system by removing electron-builder dependency

- Remove electron-builder and @electron/rebuild packages to resolve build errors
- Clean up package.json scripts that depend on electron-builder
- Fix merge conflict markers in AdminSettings.tsx and PasswordReset.tsx
- All build commands now work correctly:
  - npm run build (frontend + backend)
  - npm run build:frontend
  - npm run build:backend
  - npm run electron:package (using electron-packager)

The build system is now stable and functional without signing requirements.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
Co-authored-by: LukeGus <bugattiguy527@gmail.com>

* Mobile UI improvement

* Electron dev (#185)

* Add comprehensive Chinese internationalization support

- Implemented i18n framework with react-i18next for multi-language support
- Added Chinese (zh) and English (en) translation files with comprehensive coverage
- Localized Admin interface, authentication flows, and error messages
- Translated FileManager operations and UI elements
- Updated HomepageAuth component with localized authentication messages
- Localized LeftSidebar navigation and host management
- Added language switcher component (shown after login only)
- Configured default language as English with Chinese as secondary option
- Localized TOTPSetup two-factor authentication interface
- Updated Docker build to include translation files
- Achieved 95%+ UI localization coverage across core components

Co-Authored-By: Claude <noreply@anthropic.com>

* Extend Chinese localization coverage to Host Manager components

- Added comprehensive translations for HostManagerHostViewer component
- Localized all host management UI text including import/export features
- Translated error messages and confirmation dialogs for host operations
- Added translations for HostManagerHostEditor validation messages
- Localized connection details, organization settings, and form labels
- Fixed syntax error in FileManagerOperations component
- Achieved near-complete localization of SSH host management interface
- Updated placeholders and tooltips for better user guidance

Co-Authored-By: Claude <noreply@anthropic.com>

* Complete comprehensive Chinese localization for Termix

- Added full localization support for Tunnel components (connected/disconnected states, retry messages)
- Localized all tunnel status messages and connection errors
- Added translations for port forwarding UI elements
- Verified Server, TopNavbar, and Tab components already have complete i18n support
- Achieved 99%+ localization coverage across entire application
- All core UI components now fully support Chinese and English languages

This completes the comprehensive internationalization effort for the Termix SSH management platform.

Co-Authored-By: Claude <noreply@anthropic.com>

* Localize additional Host Manager components and authentication settings

- Added translations for all authentication options (Password, Key, SSH Private Key)
- Localized form labels in HostManagerHostEditor (Pin Connection, Enable Terminal/Tunnel/FileManager)
- Translated Upload/Update Key button states
- Localized Host Viewer and Add/Edit Host tab labels
- Added Chinese translations for all host management settings
- Fixed duplicate translation keys in JSON files

Co-Authored-By: Claude <noreply@anthropic.com>

* Extend localization coverage to UI components and common strings

- Added comprehensive common translations (online/offline, success/error, etc.)
- Localized status indicator component with all status states
- Updated FileManagerLeftSidebar toast messages for rename/delete operations
- Added translations for UI elements (close, toggle sidebar, etc.)
- Expanded placeholder translations for form inputs
- Added Chinese translations for all new common strings
- Improved consistency across component status messages

Co-Authored-By: Claude <noreply@anthropic.com>

* Complete Chinese localization for remaining UI components

- Add comprehensive Chinese translations for Host Manager component
  - Translate all form labels, buttons, and descriptions
  - Add translations for SSH configuration warnings and instructions
  - Localize tunnel connection settings and port forwarding options

- Localize SSH Tools panel
  - Translate key recording functionality
  - Add translations for settings and configuration options

- Translate homepage welcome messages and navigation elements
  - Add Chinese translations for login success messages
  - Localize "Updates & Releases" section title
  - Translate sidebar "Host Manager" button

- Fix translation key display issues
  - Remove duplicate translation keys in both language files
  - Ensure all components properly reference translation keys
  - Fix hosts.tunnelConnections key mapping

This completes the full Chinese localization of the Termix application,
achieving near 100% UI translation coverage while maintaining English
as the default language.

* Complete final Chinese localization for Host Manager tunnel configuration

- Add Chinese translations for authentication UI elements
  - Translate "Authentication", "Password", and "Key" tab labels
  - Localize SSH private key and key password fields
  - Add translations for key type selector

- Localize tunnel connection configuration descriptions
  - Translate retry attempts and retry interval descriptions
  - Add dynamic tunnel forwarding description with port parameters
  - Localize endpoint SSH configuration labels

- Fix missing translation keys
  - Add "upload" translation for file upload button
  - Ensure all FormLabel and FormDescription elements use translation keys

This completes the comprehensive Chinese localization of the entire
Termix application, achieving 100% UI translation coverage.

* Fix PR feedback: Improve Profile section translations and UX

- Fixed password reset translations in Profile section
- Moved language selector from TopNavbar to Profile page
- Added profile.selectPreferredLanguage translation key
- Improved user experience for language preferences

* Apply critical OIDC and notification system fixes while preserving i18n

- Merge OIDC authentication fixes from 3877e90:
  * Enhanced JWKS discovery mechanism with multiple backup URLs
  * Better support for non-standard OIDC providers (Authentik, etc.)
  * Improved error handling for "Failed to get user information"
- Migrate to unified Sonner toast notification system:
  * Replace custom success/error state management
  * Remove redundant alert state variables
  * Consistent user feedback across all components
- Improve code quality and function naming conventions
- PRESERVE all existing i18n functionality and Chinese translations

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix OIDC errors for "Failed to get user information"

* Fix OIDC errors for "Failed to get user information"

* Fix spelling error

* Migrate everything to alert system, update user.ts for OIDC updates.

* Fix OIDC errors for "Failed to get user information"

* Fix OIDC errors for "Failed to get user information"

* Fix spelling error

* Migrate everything to alert system, update user.ts for OIDC updates.

* Update env

* Fix users.ts and schema for override

* Convert web app to Electron desktop application

- Add Electron main process with developer tools support
- Create preload script for secure context bridge
- Configure electron-builder for packaging
- Update Vite config for Electron compatibility (base: './')
- Add environment variable support for API host configuration
- Fix i18n to use relative paths for Electron file protocol
- Restore multi-port backend architecture (8081-8085)
- Add enhanced backend startup script with port checking
- Update package.json with Electron dependencies and build scripts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Complete Electron desktop application implementation

- Add backend auto-start functionality in main process
- Fix authentication token storage for Electron environment
- Implement localStorage-based token management in Electron
- Add proper Electron environment detection via preload script
- Fix WebSocket connections for terminal functionality
- Resolve font file loading issues in packaged application
- Update API endpoints to work with backend auto-start
- Streamline build scripts with unified electron:package command
- Fix better-sqlite3 native module compatibility issues
- Ensure all services start automatically in production mode

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Remove releases folder from git and force Desktop UI.

* Improve mobile support with half-baked custom keyboard

* Fix API routing

* Upgrade mobile keyboard with more keys.

* Add cross-platform support and clean up obsolete files

- Add electron-packager scripts for Windows, macOS, and Linux
- Include universal architecture support for macOS
- Add electron:package:all for building all platforms
- Remove obsolete start-backend.sh script (replaced by Electron auto-start)
- Improve ignore patterns to exclude repo-images folder
- Add platform-specific icon configurations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix build system by removing electron-builder dependency

- Remove electron-builder and @electron/rebuild packages to resolve build errors
- Clean up package.json scripts that depend on electron-builder
- Fix merge conflict markers in AdminSettings.tsx and PasswordReset.tsx
- All build commands now work correctly:
  - npm run build (frontend + backend)
  - npm run build:frontend
  - npm run build:backend
  - npm run electron:package (using electron-packager)

The build system is now stable and functional without signing requirements.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
Co-authored-by: LukeGus <bugattiguy527@gmail.com>
Co-authored-by: Karmaa <88517757+LukeGus@users.noreply.github.com>

* Add navigation and hardcoded hosts

* Update mobile sidebar to use API, add auth and tab system to mobile.

* Update sidebar state

* Mobile support (#190)

* Add vibration to keyboard

* Fix keyboard keys

* Fix keyboard keys

* Fix keyboard keys

* Rename files, improve keyboard usability

* Improve keyboard view and fix various issues with it

* Add mobile chinese translation

* Disable OS keyboard from appearing

* Fix fit addon not resizing with "more" on keyboard

* Disable OS keyboard on terminal load

* Merge Luke and Zac

* feat: add export  option for ssh hosts (#173) (#187)

* Update issue templates

* feat: add export JSON option for SSH hosts (#173)

---------

Co-authored-by: Karmaa <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: LukeGus <bugattiguy527@gmail.com>

* feat(profile): display version number from .env in profile menu (#182)

* feat(profile): display version number from .env in profile menu

* Update version checking process

---------

Co-authored-by: LukeGus <bugattiguy527@gmail.com>

* Add pretier

* feat(auth): Add password visibility toggle to auth forms (#166)

* added hide and unhide password button

* Undo admin settings changes

---------

Co-authored-by: LukeGus <bugattiguy527@gmail.com>

* Re-added password input

* Remove encrpytion, improve logging and merge interfaces.

* Improve logging (backend and frontend) and added dedicde OIDC clear

* feat: Added option to paste private key (#203)

* Improve logging frontend/backend, fix host form being reversed.

* Improve logging more, fix credentials sync issues, migrate more to be toasts

* Improve logging more, fix credentials sync issues, migrate more to be toasts

* More error to toast migration

* Remove more inline styles and run npm updates

* Update homepage appearing over everything and terminal incorrect bg

* Improved server stat generation and UI by caching and supporting more platforms

* Update mobile app with the same stat changes and remove rate limiting

* Put user profle in its own tab, add code rabbit support

* Improve code rabbit yaml

* Update chinese translation and fix z indexs causing delay to hide

* Bump vite from 7.1.3 to 7.1.5 (#204)

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.3 to 7.1.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.5
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update read me

* Update electron builder and fix mobile terminal background

* Update logo, move translations, update electron building.

* Remove backend from electon, switching to server manager

* Add electron server configurator

* Fix backend builder on Dockerfile

* Fix langauge file for Dockerfile

* Fix architecture issues in Dockerfile

* Fix architecture issues in Dockerfile

* Fix architecture issues in Dockerfile

* Fix backend building for docker image

* Add electron builder

* Fix node starting in entrypoint and remove release from electron build

* Remove double packaing in electron build

* Fix folder nesting for electron gbuilder

* Fix native module docker build (better-sql and bcrypt)

* Fix api routes and missing translations and improve reconnection for terminals

* Update read me for new installation method

* Update CONTRIBUTING.md with color scheme

* Fix terrminal not closing afer 3 tries

* Fix electronm api routing, fikx ssh not connecting, and OIDC redirect errors

* Fix more electron API issues (ssh/oidc), make server manager force API check, and login saving.

* Add electron API routes

* Fix more electron APi routes and issues

* Hide admin settings on electron and fix server manager URl verification

* Hide admin settings on electron and fix server manager URl verification

* Fix admin setting visiblity on electron

* Add links to docs in respective places

* Migrate all getCookies to use main-axios.

* Migrate all isElectron to use main-axios.

* Clean up backend files

* Clean up frontend files and read me translations

* Run prettier

* Fix terminal in web, and update translations and prep for release.

* Update API to work on devs and remove random letter

* Run prettier

* Update read me for release

* Update read me for release

* Fixed delete issue (ready for release)

* Ensure retention days for artifact upload are set

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: starry <115192496+sky22333@users.noreply.github.com>
Co-authored-by: ZacharyZcR <PayasoNorahC@protonmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
Co-authored-by: Shivam Kumar <155747305+maishivamhoo123@users.noreply.github.com>
Co-authored-by: Abhilash Gandhamalla <150357125+AbhilashG12@users.noreply.github.com>
Co-authored-by: jedi04 <78037206+jedi04@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit was merged in pull request #221.
This commit is contained in:
Karmaa
2025-09-12 14:42:00 -05:00
committed by GitHub
parent d85fb26a5d
commit 5cd9de9ac5
187 changed files with 42370 additions and 20790 deletions

View File

@@ -0,0 +1,489 @@
import React, { useState } from "react";
import { useSidebar } from "@/components/ui/sidebar.tsx";
import { Button } from "@/components/ui/button.tsx";
import { ChevronDown, ChevronUpIcon, Hammer } from "lucide-react";
import { Tab } from "@/ui/Desktop/Navigation/Tabs/Tab.tsx";
import { useTabs } from "@/ui/Desktop/Navigation/Tabs/TabContext.tsx";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion.tsx";
import { Input } from "@/components/ui/input.tsx";
import { Checkbox } from "@/components/ui/checkbox.tsx";
import { Separator } from "@/components/ui/separator.tsx";
import { useTranslation } from "react-i18next";
import { TabDropdown } from "@/ui/Desktop/Navigation/Tabs/TabDropdown.tsx";
import { getCookie, setCookie } from "@/ui/main-axios.ts";
interface TopNavbarProps {
isTopbarOpen: boolean;
setIsTopbarOpen: (open: boolean) => void;
}
export function TopNavbar({
isTopbarOpen,
setIsTopbarOpen,
}: TopNavbarProps): React.ReactElement {
const { state } = useSidebar();
const {
tabs,
currentTab,
setCurrentTab,
setSplitScreenTab,
removeTab,
allSplitScreenTab,
} = useTabs() as any;
const leftPosition = state === "collapsed" ? "26px" : "264px";
const { t } = useTranslation();
const [toolsSheetOpen, setToolsSheetOpen] = useState(false);
const [isRecording, setIsRecording] = useState(false);
const [selectedTabIds, setSelectedTabIds] = useState<number[]>([]);
const handleTabActivate = (tabId: number) => {
setCurrentTab(tabId);
};
const handleTabSplit = (tabId: number) => {
setSplitScreenTab(tabId);
};
const handleTabClose = (tabId: number) => {
removeTab(tabId);
};
const handleTabToggle = (tabId: number) => {
setSelectedTabIds((prev) =>
prev.includes(tabId)
? prev.filter((id) => id !== tabId)
: [...prev, tabId],
);
};
const handleStartRecording = () => {
setIsRecording(true);
setTimeout(() => {
const input = document.getElementById(
"ssh-tools-input",
) as HTMLInputElement;
if (input) input.focus();
}, 100);
};
const handleStopRecording = () => {
setIsRecording(false);
setSelectedTabIds([]);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (selectedTabIds.length === 0) return;
let commandToSend = "";
if (e.ctrlKey || e.metaKey) {
if (e.key === "c") {
commandToSend = "\x03"; // Ctrl+C (SIGINT)
e.preventDefault();
} else if (e.key === "d") {
commandToSend = "\x04"; // Ctrl+D (EOF)
e.preventDefault();
} else if (e.key === "l") {
commandToSend = "\x0c"; // Ctrl+L (clear screen)
e.preventDefault();
} else if (e.key === "u") {
commandToSend = "\x15"; // Ctrl+U (clear line)
e.preventDefault();
} else if (e.key === "k") {
commandToSend = "\x0b"; // Ctrl+K (clear from cursor to end)
e.preventDefault();
} else if (e.key === "a") {
commandToSend = "\x01"; // Ctrl+A (move to beginning of line)
e.preventDefault();
} else if (e.key === "e") {
commandToSend = "\x05"; // Ctrl+E (move to end of line)
e.preventDefault();
} else if (e.key === "w") {
commandToSend = "\x17"; // Ctrl+W (delete word before cursor)
e.preventDefault();
}
} else if (e.key === "Enter") {
commandToSend = "\n";
e.preventDefault();
} else if (e.key === "Backspace") {
commandToSend = "\x08"; // Backspace
e.preventDefault();
} else if (e.key === "Delete") {
commandToSend = "\x7f"; // Delete
e.preventDefault();
} else if (e.key === "Tab") {
commandToSend = "\x09"; // Tab
e.preventDefault();
} else if (e.key === "Escape") {
commandToSend = "\x1b"; // Escape
e.preventDefault();
} else if (e.key === "ArrowUp") {
commandToSend = "\x1b[A"; // Up arrow
e.preventDefault();
} else if (e.key === "ArrowDown") {
commandToSend = "\x1b[B"; // Down arrow
e.preventDefault();
} else if (e.key === "ArrowLeft") {
commandToSend = "\x1b[D"; // Left arrow
e.preventDefault();
} else if (e.key === "ArrowRight") {
commandToSend = "\x1b[C"; // Right arrow
e.preventDefault();
} else if (e.key === "Home") {
commandToSend = "\x1b[H"; // Home
e.preventDefault();
} else if (e.key === "End") {
commandToSend = "\x1b[F"; // End
e.preventDefault();
} else if (e.key === "PageUp") {
commandToSend = "\x1b[5~"; // Page Up
e.preventDefault();
} else if (e.key === "PageDown") {
commandToSend = "\x1b[6~"; // Page Down
e.preventDefault();
} else if (e.key === "Insert") {
commandToSend = "\x1b[2~"; // Insert
e.preventDefault();
} else if (e.key === "F1") {
commandToSend = "\x1bOP"; // F1
e.preventDefault();
} else if (e.key === "F2") {
commandToSend = "\x1bOQ"; // F2
e.preventDefault();
} else if (e.key === "F3") {
commandToSend = "\x1bOR"; // F3
e.preventDefault();
} else if (e.key === "F4") {
commandToSend = "\x1bOS"; // F4
e.preventDefault();
} else if (e.key === "F5") {
commandToSend = "\x1b[15~"; // F5
e.preventDefault();
} else if (e.key === "F6") {
commandToSend = "\x1b[17~"; // F6
e.preventDefault();
} else if (e.key === "F7") {
commandToSend = "\x1b[18~"; // F7
e.preventDefault();
} else if (e.key === "F8") {
commandToSend = "\x1b[19~"; // F8
e.preventDefault();
} else if (e.key === "F9") {
commandToSend = "\x1b[20~"; // F9
e.preventDefault();
} else if (e.key === "F10") {
commandToSend = "\x1b[21~"; // F10
e.preventDefault();
} else if (e.key === "F11") {
commandToSend = "\x1b[23~"; // F11
e.preventDefault();
} else if (e.key === "F12") {
commandToSend = "\x1b[24~"; // F12
e.preventDefault();
}
if (commandToSend) {
selectedTabIds.forEach((tabId) => {
const tab = tabs.find((t: any) => t.id === tabId);
if (tab?.terminalRef?.current?.sendInput) {
tab.terminalRef.current.sendInput(commandToSend);
}
});
}
};
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (selectedTabIds.length === 0) return;
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
const char = e.key;
selectedTabIds.forEach((tabId) => {
const tab = tabs.find((t: any) => t.id === tabId);
if (tab?.terminalRef?.current?.sendInput) {
tab.terminalRef.current.sendInput(char);
}
});
}
};
const isSplitScreenActive =
Array.isArray(allSplitScreenTab) && allSplitScreenTab.length > 0;
const currentTabObj = tabs.find((t: any) => t.id === currentTab);
const currentTabIsHome = currentTabObj?.type === "home";
const currentTabIsSshManager = currentTabObj?.type === "ssh_manager";
const currentTabIsAdmin = currentTabObj?.type === "admin";
const currentTabIsUserProfile = currentTabObj?.type === "user_profile";
const terminalTabs = tabs.filter((tab: any) => tab.type === "terminal");
const updateRightClickCopyPaste = (checked: boolean) => {
setCookie("rightClickCopyPaste", checked.toString());
};
return (
<div>
<div
className="fixed z-10 h-[50px] bg-dark-bg border-2 border-dark-border rounded-lg transition-all duration-200 ease-linear flex flex-row transform-none m-0 p-0"
style={{
top: isTopbarOpen ? "0.5rem" : "-3rem",
left: leftPosition,
right: "17px",
}}
>
<div className="h-full p-1 pr-2 border-r-2 border-dark-border w-[calc(100%-6rem)] flex items-center overflow-x-auto overflow-y-hidden gap-2 thin-scrollbar">
{tabs.map((tab: any) => {
const isActive = tab.id === currentTab;
const isSplit =
Array.isArray(allSplitScreenTab) &&
allSplitScreenTab.includes(tab.id);
const isTerminal = tab.type === "terminal";
const isServer = tab.type === "server";
const isFileManager = tab.type === "file_manager";
const isSshManager = tab.type === "ssh_manager";
const isAdmin = tab.type === "admin";
const isUserProfile = tab.type === "user_profile";
const isSplittable = isTerminal || isServer || isFileManager;
const isSplitButtonDisabled =
(isActive && !isSplitScreenActive) ||
((allSplitScreenTab?.length || 0) >= 3 && !isSplit);
const disableSplit =
!isSplittable ||
isSplitButtonDisabled ||
isActive ||
currentTabIsHome ||
currentTabIsSshManager ||
currentTabIsAdmin ||
currentTabIsUserProfile;
const disableActivate =
isSplit ||
((tab.type === "home" ||
tab.type === "ssh_manager" ||
tab.type === "admin" ||
tab.type === "user_profile") &&
isSplitScreenActive);
const disableClose = (isSplitScreenActive && isActive) || isSplit;
return (
<Tab
key={tab.id}
tabType={tab.type}
title={tab.title}
isActive={isActive}
onActivate={() => handleTabActivate(tab.id)}
onClose={
isTerminal ||
isServer ||
isFileManager ||
isSshManager ||
isAdmin ||
isUserProfile
? () => handleTabClose(tab.id)
: undefined
}
onSplit={
isSplittable ? () => handleTabSplit(tab.id) : undefined
}
canSplit={isSplittable}
canClose={
isTerminal ||
isServer ||
isFileManager ||
isSshManager ||
isAdmin ||
isUserProfile
}
disableActivate={disableActivate}
disableSplit={disableSplit}
disableClose={disableClose}
/>
);
})}
</div>
<div className="flex items-center justify-center gap-2 flex-1 px-2">
<TabDropdown />
<Button
variant="outline"
className="w-[30px] h-[30px]"
title={t("nav.tools")}
onClick={() => setToolsSheetOpen(true)}
>
<Hammer className="h-4 w-4" />
</Button>
<Button
variant="outline"
onClick={() => setIsTopbarOpen(false)}
className="w-[30px] h-[30px]"
>
<ChevronUpIcon />
</Button>
</div>
</div>
{!isTopbarOpen && (
<div
onClick={() => setIsTopbarOpen(true)}
className="absolute top-0 left-0 w-full h-[10px] bg-dark-bg cursor-pointer z-20 flex items-center justify-center rounded-bl-md rounded-br-md"
>
<ChevronDown size={10} />
</div>
)}
{toolsSheetOpen && (
<div
className="fixed top-0 left-0 right-0 bottom-0 z-[999999] flex justify-end pointer-events-auto isolate"
style={{
transform: "translateZ(0)",
}}
>
<div
className="flex-1 cursor-pointer"
onClick={() => setToolsSheetOpen(false)}
/>
<div
className="w-[400px] h-full bg-dark-bg border-l-2 border-dark-border flex flex-col shadow-2xl relative isolate z-[999999]"
style={{
boxShadow: "-4px 0 20px rgba(0, 0, 0, 0.5)",
transform: "translateZ(0)",
}}
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between p-4 border-b border-dark-border">
<h2 className="text-lg font-semibold text-white">
{t("sshTools.title")}
</h2>
<Button
variant="outline"
size="sm"
onClick={() => setToolsSheetOpen(false)}
className="h-8 w-8 p-0 hover:bg-red-500 hover:text-white transition-colors flex items-center justify-center"
title={t("sshTools.closeTools")}
>
<span className="text-lg font-bold leading-none">×</span>
</Button>
</div>
<div className="flex-1 overflow-y-auto p-4">
<div className="space-y-4">
<h1 className="font-semibold">{t("sshTools.keyRecording")}</h1>
<div className="space-y-4">
<div className="space-y-4">
<div className="flex gap-2">
{!isRecording ? (
<Button
onClick={handleStartRecording}
className="flex-1"
variant="outline"
>
{t("sshTools.startKeyRecording")}
</Button>
) : (
<Button
onClick={handleStopRecording}
className="flex-1"
variant="destructive"
>
{t("sshTools.stopKeyRecording")}
</Button>
)}
</div>
{isRecording && (
<>
<div className="space-y-2">
<label className="text-sm font-medium text-white">
{t("sshTools.selectTerminals")}
</label>
<div className="flex flex-wrap gap-2 max-h-32 overflow-y-auto mt-2">
{terminalTabs.map((tab) => (
<Button
key={tab.id}
type="button"
variant="outline"
size="sm"
className={`rounded-full px-3 py-1 text-xs flex items-center gap-1 ${
selectedTabIds.includes(tab.id)
? "text-white bg-gray-700"
: "text-gray-500"
}`}
onClick={() => handleTabToggle(tab.id)}
>
{tab.title}
</Button>
))}
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-white">
{t("sshTools.typeCommands")}
</label>
<Input
id="ssh-tools-input"
placeholder={t("placeholders.typeHere")}
onKeyDown={handleKeyDown}
onKeyPress={handleKeyPress}
className="font-mono mt-2"
disabled={selectedTabIds.length === 0}
readOnly
/>
<p className="text-xs text-muted-foreground">
{t("sshTools.commandsWillBeSent", {
count: selectedTabIds.length,
})}
</p>
</div>
</>
)}
</div>
</div>
<Separator className="my-4" />
<h1 className="font-semibold">{t("sshTools.settings")}</h1>
<div className="flex items-center space-x-2">
<Checkbox
id="enable-copy-paste"
onCheckedChange={updateRightClickCopyPaste}
defaultChecked={getCookie("rightClickCopyPaste") === "true"}
/>
<label
htmlFor="enable-copy-paste"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-white"
>
{t("sshTools.enableRightClickCopyPaste")}
</label>
</div>
<Separator className="my-4" />
<p className="pt-2 pb-2 text-sm text-gray-500">
{t("sshTools.shareIdeas")}{" "}
<a
href="https://github.com/LukeGus/Termix/issues/new"
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
GitHub
</a>
!
</p>
</div>
</div>
</div>
</div>
)}
</div>
);
}