Compare commits
19 Commits
v0-m0-done
...
v0-m4-done
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b25f805cd | ||
|
|
776ddc1a53 | ||
|
|
c3369b8e48 | ||
|
|
20ee48db32 | ||
|
|
2b4f498259 | ||
|
|
614d31fa71 | ||
|
|
ceed19d517 | ||
|
|
2ea712db36 | ||
|
|
3c158269bf | ||
|
|
e2a8b874d7 | ||
|
|
71a2f2e868 | ||
|
|
6a4bcb75eb | ||
|
|
582c57bc5f | ||
|
|
01762422e9 | ||
|
|
f8a81ebe36 | ||
|
|
87b0f60569 | ||
|
|
09c54313af | ||
|
|
fba7336f69 | ||
|
|
6d147894c5 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build/
|
||||||
@@ -10,18 +10,36 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
find_package(Qt6 6.2 REQUIRED COMPONENTS Widgets)
|
find_package(Qt6 6.2 REQUIRED COMPONENTS Widgets Sql)
|
||||||
|
|
||||||
qt_standard_project_setup()
|
qt_standard_project_setup()
|
||||||
|
|
||||||
|
add_subdirectory(third_party/KodoTerm)
|
||||||
|
|
||||||
add_executable(orbithub
|
add_executable(orbithub
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
src/profile_dialog.cpp
|
||||||
|
src/profile_dialog.h
|
||||||
|
src/profile_repository.cpp
|
||||||
|
src/profile_repository.h
|
||||||
src/profiles_window.cpp
|
src/profiles_window.cpp
|
||||||
src/profiles_window.h
|
src/profiles_window.h
|
||||||
|
src/session_backend.h
|
||||||
|
src/session_backend_factory.cpp
|
||||||
|
src/session_backend_factory.h
|
||||||
|
src/session_tab.cpp
|
||||||
|
src/session_tab.h
|
||||||
|
src/terminal_view.cpp
|
||||||
|
src/terminal_view.h
|
||||||
src/session_window.cpp
|
src/session_window.cpp
|
||||||
src/session_window.h
|
src/session_window.h
|
||||||
|
src/ssh_session_backend.cpp
|
||||||
|
src/ssh_session_backend.h
|
||||||
|
src/unsupported_session_backend.cpp
|
||||||
|
src/unsupported_session_backend.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(orbithub PRIVATE Qt6::Widgets)
|
target_link_libraries(orbithub PRIVATE Qt6::Widgets Qt6::Sql)
|
||||||
|
target_link_libraries(orbithub PRIVATE KodoTerm::KodoTerm)
|
||||||
|
|
||||||
install(TARGETS orbithub RUNTIME DESTINATION bin)
|
install(TARGETS orbithub RUNTIME DESTINATION bin)
|
||||||
|
|||||||
59
docs/BUILDING.md
Normal file
59
docs/BUILDING.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Building OrbitHub (C++ / Qt6 Widgets)
|
||||||
|
|
||||||
|
Run all commands from the repository root unless noted.
|
||||||
|
|
||||||
|
## Linux (Ubuntu / Mint)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y \
|
||||||
|
build-essential cmake ninja-build git pkg-config \
|
||||||
|
qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools \
|
||||||
|
openssh-client
|
||||||
|
|
||||||
|
cmake -S . -B build -G Ninja
|
||||||
|
cmake --build build
|
||||||
|
./build/orbithub
|
||||||
|
```
|
||||||
|
|
||||||
|
## macOS (Homebrew)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcode-select --install
|
||||||
|
brew update
|
||||||
|
brew install cmake ninja pkg-config qt@6 openssh
|
||||||
|
|
||||||
|
cmake -S . -B build -G Ninja -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6)"
|
||||||
|
cmake --build build
|
||||||
|
./build/orbithub
|
||||||
|
```
|
||||||
|
|
||||||
|
## Windows 11 (PowerShell + MSVC + vcpkg)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
winget install -e --id Git.Git
|
||||||
|
winget install -e --id Kitware.CMake
|
||||||
|
winget install -e --id Ninja-build.Ninja
|
||||||
|
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
|
||||||
|
winget install -e --id Microsoft.VisualStudio.2022.BuildTools `
|
||||||
|
--override "--quiet --wait --norestart --add Microsoft.VisualStudio.Workload.VCTools"
|
||||||
|
```
|
||||||
|
|
||||||
|
Open a new terminal after installs, then:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git clone https://github.com/microsoft/vcpkg C:\dev\vcpkg
|
||||||
|
C:\dev\vcpkg\bootstrap-vcpkg.bat
|
||||||
|
C:\dev\vcpkg\vcpkg.exe install qtbase:x64-windows
|
||||||
|
|
||||||
|
cmake -S . -B build -G Ninja `
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||||
|
cmake --build build
|
||||||
|
.\build\orbithub.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- OrbitHub currently requires Qt6 Widgets and CMake 3.21+.
|
||||||
|
- Milestone 3 SSH sessions require an `ssh` client available on `PATH`.
|
||||||
|
- If Qt is installed in a custom location, pass `-DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/<toolchain>` to CMake.
|
||||||
@@ -54,3 +54,40 @@ OrbitHub uses a two-window model:
|
|||||||
- Profiles CRUD
|
- Profiles CRUD
|
||||||
- Connect loads profile name into tab
|
- Connect loads profile name into tab
|
||||||
- Tag: v0-m1-done
|
- Tag: v0-m1-done
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone 2
|
||||||
|
|
||||||
|
- Extend profiles schema (`host`, `port`, `username`, `protocol`, `auth_mode`)
|
||||||
|
- Replace prompt-based create/edit with a structured profile dialog form
|
||||||
|
- Connect loads full profile details into session tab
|
||||||
|
- Session lifecycle states in UI (`Connecting`, `Connected`, `Failed`) with non-blocking updates
|
||||||
|
- Tag: v0-m2-done
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone 3
|
||||||
|
|
||||||
|
- Real SSH backend using native `ssh` process (connect, disconnect, reconnect)
|
||||||
|
- Protocol backend abstraction with worker-thread execution
|
||||||
|
- RDP/VNC explicitly marked as not implemented in session UX
|
||||||
|
- Connect-time credential prompts (password/private key path) with no secret storage in DB
|
||||||
|
- Session tab controls: `Connect`, `Disconnect`, `Reconnect`, `Copy Error`
|
||||||
|
- Per-session timestamped event log and user-friendly error mapping
|
||||||
|
- Profile schema extended with `private_key_path` and `known_hosts_policy`
|
||||||
|
- Tag: v0-m3-done
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone 4 (Draft)
|
||||||
|
|
||||||
|
- Make SSH sessions actually usable as interactive sessions inside OrbitHub
|
||||||
|
- Replace placeholder surface with interactive SSH terminal panel (stream output + send input)
|
||||||
|
- Add SSH host-key trust prompt flow for `Ask` policy
|
||||||
|
- Improve auth flow UX for password / private key at connect time
|
||||||
|
- Preserve responsive UI under active session output
|
||||||
|
- Add terminal/session utilities (`Clear Terminal`, improved output visibility)
|
||||||
|
- Keep session controls stable (`Connect`, `Disconnect`, `Reconnect`, `Copy Error`)
|
||||||
|
- Add validation and diagnostics coverage for host-key, auth, and reconnect behavior
|
||||||
|
- Planned Tag: v0-m4-done (after completion approval)
|
||||||
|
|||||||
@@ -11,7 +11,70 @@ Delivered:
|
|||||||
- `SessionWindow` (`QMainWindow`) with `QTabWidget`
|
- `SessionWindow` (`QMainWindow`) with `QTabWidget`
|
||||||
- Placeholder tab content showing `OrbitHub Native Surface`
|
- Placeholder tab content showing `OrbitHub Native Surface`
|
||||||
- `main.cpp` wiring for application startup
|
- `main.cpp` wiring for application startup
|
||||||
|
- Cross-platform build command guide in `docs/BUILDING.md`
|
||||||
|
|
||||||
Git:
|
Git:
|
||||||
- Branch: `milestone-0-restart-cpp`
|
|
||||||
- Tag: `v0-m0-done`
|
- Tag: `v0-m0-done`
|
||||||
|
|
||||||
|
## Milestone 1 - Storage and CRUD
|
||||||
|
|
||||||
|
Status: Completed
|
||||||
|
|
||||||
|
Delivered:
|
||||||
|
- SQLite integration via Qt SQL (`QSQLITE`)
|
||||||
|
- Persistent profile database bootstrap (`profiles` table)
|
||||||
|
- Profiles CRUD (New / Edit / Delete) in `ProfilesWindow`
|
||||||
|
- Search-backed profile listing from storage
|
||||||
|
- Double-click connect opens `SessionWindow` tab with selected profile name
|
||||||
|
|
||||||
|
Git:
|
||||||
|
- Tag: `v0-m1-done`
|
||||||
|
|
||||||
|
## Milestone 2 - Profile Details and Connect Lifecycle
|
||||||
|
|
||||||
|
Status: Completed
|
||||||
|
|
||||||
|
Delivered:
|
||||||
|
- SQLite schema migration for profile details (`host`, `port`, `username`, `protocol`, `auth_mode`)
|
||||||
|
- New `ProfileDialog` form for New/Edit profile workflows
|
||||||
|
- Profiles list now shows endpoint metadata and supports search by name or host
|
||||||
|
- Connect now loads complete profile details into `SessionWindow`
|
||||||
|
- Session tab lifecycle status updates (`Connecting`, `Connected`, `Failed`) via non-blocking timer flow
|
||||||
|
|
||||||
|
Git:
|
||||||
|
- Tag: `v0-m2-done`
|
||||||
|
|
||||||
|
## Milestone 3 - Real SSH Backend and Session Controls
|
||||||
|
|
||||||
|
Status: Completed
|
||||||
|
|
||||||
|
Delivered:
|
||||||
|
- Backend architecture introduced (`SessionBackend` + protocol-specific implementations)
|
||||||
|
- Worker-thread backend execution for connection lifecycle operations
|
||||||
|
- Real SSH process backend (`ssh`) with connect/disconnect/reconnect
|
||||||
|
- Unsupported protocol backend with explicit not-implemented messaging (RDP/VNC)
|
||||||
|
- Session tab controls: `Connect`, `Disconnect`, `Reconnect`, `Copy Error`
|
||||||
|
- Connect-time credential flow (password prompt / private-key path selection)
|
||||||
|
- Session event log pane with timestamps and user-friendly error mapping
|
||||||
|
- SQLite profile schema migration for `private_key_path` and `known_hosts_policy`
|
||||||
|
|
||||||
|
Git:
|
||||||
|
- Tag: `v0-m3-done`
|
||||||
|
|
||||||
|
## Milestone 4 - Interactive SSH Session UX
|
||||||
|
|
||||||
|
Status: Completed
|
||||||
|
|
||||||
|
Delivered:
|
||||||
|
- Embedded interactive SSH terminal using `KodoTerm` + vendored `libvterm`
|
||||||
|
- Native in-terminal typing for SSH sessions (no separate input box)
|
||||||
|
- ANSI/color rendering with selectable terminal themes (`Dark`, `Light`, `Solarized Dark`)
|
||||||
|
- Cross-platform SSH auth path improvements (`ssh-askpass` handling and host-key policy wiring)
|
||||||
|
- Session UX simplification: auto-connect on tab open, disconnect on tab close
|
||||||
|
- Tab-state indicators via tab color and state suffix (`Connecting`, `Connected`, `Disconnected`, `Failed`)
|
||||||
|
- Right-click tab menu for `Disconnect`, `Reconnect`, `Theme`, and `Clear`
|
||||||
|
- Collapsible events panel retained as primary diagnostics surface; inline detail/status banners removed
|
||||||
|
- Terminal behavior polish: better fixed-width font selection, cursor visibility, backspace handling, and terminal-size negotiation stability
|
||||||
|
|
||||||
|
Git:
|
||||||
|
- Tag: `v0-m4-done` (pending push)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
Q_INIT_RESOURCE(KodoTermThemes);
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
ProfilesWindow window;
|
ProfilesWindow window;
|
||||||
|
|||||||
196
src/profile_dialog.cpp
Normal file
196
src/profile_dialog.cpp
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
#include "profile_dialog.h"
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSignalBlocker>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int standardPortForProtocol(const QString& protocol)
|
||||||
|
{
|
||||||
|
if (protocol == QStringLiteral("RDP")) {
|
||||||
|
return 3389;
|
||||||
|
}
|
||||||
|
if (protocol == QStringLiteral("VNC")) {
|
||||||
|
return 5900;
|
||||||
|
}
|
||||||
|
return 22; // SSH default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileDialog::ProfileDialog(QWidget* parent)
|
||||||
|
: QDialog(parent),
|
||||||
|
m_nameInput(new QLineEdit(this)),
|
||||||
|
m_hostInput(new QLineEdit(this)),
|
||||||
|
m_portInput(new QSpinBox(this)),
|
||||||
|
m_usernameInput(new QLineEdit(this)),
|
||||||
|
m_protocolInput(new QComboBox(this)),
|
||||||
|
m_authModeInput(new QComboBox(this)),
|
||||||
|
m_privateKeyPathInput(new QLineEdit(this)),
|
||||||
|
m_browsePrivateKeyButton(new QPushButton(QStringLiteral("Browse"), this)),
|
||||||
|
m_knownHostsPolicyInput(new QComboBox(this))
|
||||||
|
{
|
||||||
|
resize(520, 340);
|
||||||
|
|
||||||
|
auto* layout = new QVBoxLayout(this);
|
||||||
|
auto* form = new QFormLayout();
|
||||||
|
|
||||||
|
m_nameInput->setPlaceholderText(QStringLiteral("Production Bastion"));
|
||||||
|
m_hostInput->setPlaceholderText(QStringLiteral("example.internal"));
|
||||||
|
m_portInput->setRange(1, 65535);
|
||||||
|
m_portInput->setValue(22);
|
||||||
|
m_usernameInput->setPlaceholderText(QStringLiteral("deploy"));
|
||||||
|
|
||||||
|
m_protocolInput->addItems({QStringLiteral("SSH"), QStringLiteral("RDP"), QStringLiteral("VNC")});
|
||||||
|
m_authModeInput->addItems({QStringLiteral("Password"), QStringLiteral("Private Key")});
|
||||||
|
m_knownHostsPolicyInput->addItems(
|
||||||
|
{QStringLiteral("Ask"), QStringLiteral("Strict"), QStringLiteral("Accept New"), QStringLiteral("Ignore")});
|
||||||
|
|
||||||
|
m_privateKeyPathInput->setPlaceholderText(QStringLiteral("/home/user/.ssh/id_ed25519"));
|
||||||
|
|
||||||
|
auto* privateKeyRow = new QWidget(this);
|
||||||
|
auto* privateKeyLayout = new QHBoxLayout(privateKeyRow);
|
||||||
|
privateKeyLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
privateKeyLayout->addWidget(m_privateKeyPathInput, 1);
|
||||||
|
privateKeyLayout->addWidget(m_browsePrivateKeyButton);
|
||||||
|
|
||||||
|
connect(m_browsePrivateKeyButton,
|
||||||
|
&QPushButton::clicked,
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
|
const QString selected = QFileDialog::getOpenFileName(this,
|
||||||
|
QStringLiteral("Select Private Key"),
|
||||||
|
QString(),
|
||||||
|
QStringLiteral("All Files (*)"));
|
||||||
|
if (!selected.isEmpty()) {
|
||||||
|
m_privateKeyPathInput->setText(selected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_protocolInput,
|
||||||
|
&QComboBox::currentTextChanged,
|
||||||
|
this,
|
||||||
|
[this](const QString& protocol) {
|
||||||
|
m_portInput->setValue(standardPortForProtocol(protocol));
|
||||||
|
refreshAuthFields();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_authModeInput,
|
||||||
|
&QComboBox::currentTextChanged,
|
||||||
|
this,
|
||||||
|
[this](const QString&) { refreshAuthFields(); });
|
||||||
|
|
||||||
|
form->addRow(QStringLiteral("Name"), m_nameInput);
|
||||||
|
form->addRow(QStringLiteral("Host"), m_hostInput);
|
||||||
|
form->addRow(QStringLiteral("Port"), m_portInput);
|
||||||
|
form->addRow(QStringLiteral("Username"), m_usernameInput);
|
||||||
|
form->addRow(QStringLiteral("Protocol"), m_protocolInput);
|
||||||
|
form->addRow(QStringLiteral("Auth Mode"), m_authModeInput);
|
||||||
|
form->addRow(QStringLiteral("Private Key"), privateKeyRow);
|
||||||
|
form->addRow(QStringLiteral("Known Hosts"), m_knownHostsPolicyInput);
|
||||||
|
|
||||||
|
auto* note = new QLabel(
|
||||||
|
QStringLiteral("Passwords are requested at connect time and are not stored."),
|
||||||
|
this);
|
||||||
|
note->setWordWrap(true);
|
||||||
|
|
||||||
|
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
|
||||||
|
layout->addLayout(form);
|
||||||
|
layout->addWidget(note);
|
||||||
|
layout->addWidget(buttons);
|
||||||
|
|
||||||
|
refreshAuthFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileDialog::setDialogTitle(const QString& title)
|
||||||
|
{
|
||||||
|
setWindowTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileDialog::setProfile(const Profile& profile)
|
||||||
|
{
|
||||||
|
m_nameInput->setText(profile.name);
|
||||||
|
m_hostInput->setText(profile.host);
|
||||||
|
m_portInput->setValue(profile.port > 0 ? profile.port : 22);
|
||||||
|
m_usernameInput->setText(profile.username);
|
||||||
|
m_privateKeyPathInput->setText(profile.privateKeyPath);
|
||||||
|
|
||||||
|
const int protocolIndex = m_protocolInput->findText(profile.protocol);
|
||||||
|
{
|
||||||
|
// Keep stored custom port when loading an existing profile.
|
||||||
|
const QSignalBlocker blocker(m_protocolInput);
|
||||||
|
m_protocolInput->setCurrentIndex(protocolIndex >= 0 ? protocolIndex : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int authModeIndex = m_authModeInput->findText(profile.authMode);
|
||||||
|
m_authModeInput->setCurrentIndex(authModeIndex >= 0 ? authModeIndex : 0);
|
||||||
|
|
||||||
|
const int knownHostsIndex = m_knownHostsPolicyInput->findText(profile.knownHostsPolicy);
|
||||||
|
m_knownHostsPolicyInput->setCurrentIndex(knownHostsIndex >= 0 ? knownHostsIndex : 0);
|
||||||
|
|
||||||
|
refreshAuthFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
Profile ProfileDialog::profile() const
|
||||||
|
{
|
||||||
|
Profile profile;
|
||||||
|
profile.id = -1;
|
||||||
|
profile.name = m_nameInput->text().trimmed();
|
||||||
|
profile.host = m_hostInput->text().trimmed();
|
||||||
|
profile.port = m_portInput->value();
|
||||||
|
profile.username = m_usernameInput->text().trimmed();
|
||||||
|
profile.protocol = m_protocolInput->currentText();
|
||||||
|
profile.authMode = m_authModeInput->currentText();
|
||||||
|
profile.privateKeyPath = m_privateKeyPathInput->text().trimmed();
|
||||||
|
profile.knownHostsPolicy = m_knownHostsPolicyInput->currentText();
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileDialog::accept()
|
||||||
|
{
|
||||||
|
if (m_nameInput->text().trimmed().isEmpty()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Validation Error"),
|
||||||
|
QStringLiteral("Profile name is required."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hostInput->text().trimmed().isEmpty()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Validation Error"),
|
||||||
|
QStringLiteral("Host is required."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_protocolInput->currentText() == QStringLiteral("SSH")
|
||||||
|
&& m_usernameInput->text().trimmed().isEmpty()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Validation Error"),
|
||||||
|
QStringLiteral("Username is required for SSH profiles."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileDialog::refreshAuthFields()
|
||||||
|
{
|
||||||
|
const bool isSsh = m_protocolInput->currentText() == QStringLiteral("SSH");
|
||||||
|
const bool isPrivateKey = m_authModeInput->currentText() == QStringLiteral("Private Key");
|
||||||
|
|
||||||
|
m_authModeInput->setEnabled(isSsh);
|
||||||
|
m_privateKeyPathInput->setEnabled(isSsh && isPrivateKey);
|
||||||
|
m_browsePrivateKeyButton->setEnabled(isSsh && isPrivateKey);
|
||||||
|
m_knownHostsPolicyInput->setEnabled(isSsh);
|
||||||
|
}
|
||||||
41
src/profile_dialog.h
Normal file
41
src/profile_dialog.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef ORBITHUB_PROFILE_DIALOG_H
|
||||||
|
#define ORBITHUB_PROFILE_DIALOG_H
|
||||||
|
|
||||||
|
#include "profile_repository.h"
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QLineEdit;
|
||||||
|
class QPushButton;
|
||||||
|
class QSpinBox;
|
||||||
|
|
||||||
|
class ProfileDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ProfileDialog(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void setDialogTitle(const QString& title);
|
||||||
|
void setProfile(const Profile& profile);
|
||||||
|
Profile profile() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void accept() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLineEdit* m_nameInput;
|
||||||
|
QLineEdit* m_hostInput;
|
||||||
|
QSpinBox* m_portInput;
|
||||||
|
QLineEdit* m_usernameInput;
|
||||||
|
QComboBox* m_protocolInput;
|
||||||
|
QComboBox* m_authModeInput;
|
||||||
|
QLineEdit* m_privateKeyPathInput;
|
||||||
|
QPushButton* m_browsePrivateKeyButton;
|
||||||
|
QComboBox* m_knownHostsPolicyInput;
|
||||||
|
|
||||||
|
void refreshAuthFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
323
src/profile_repository.cpp
Normal file
323
src/profile_repository.cpp
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
#include "profile_repository.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QString buildDatabasePath()
|
||||||
|
{
|
||||||
|
QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
|
if (appDataPath.isEmpty()) {
|
||||||
|
appDataPath = QDir::currentPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir dataDir(appDataPath);
|
||||||
|
dataDir.mkpath(QStringLiteral("."));
|
||||||
|
|
||||||
|
return dataDir.filePath(QStringLiteral("orbithub_profiles.sqlite"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindProfileFields(QSqlQuery& query, const Profile& profile)
|
||||||
|
{
|
||||||
|
query.addBindValue(profile.name.trimmed());
|
||||||
|
query.addBindValue(profile.host.trimmed());
|
||||||
|
query.addBindValue(profile.port);
|
||||||
|
query.addBindValue(profile.username.trimmed());
|
||||||
|
query.addBindValue(profile.protocol.trimmed());
|
||||||
|
query.addBindValue(profile.authMode.trimmed());
|
||||||
|
query.addBindValue(profile.privateKeyPath.trimmed());
|
||||||
|
query.addBindValue(profile.knownHostsPolicy.trimmed().isEmpty()
|
||||||
|
? QStringLiteral("Ask")
|
||||||
|
: profile.knownHostsPolicy.trimmed());
|
||||||
|
}
|
||||||
|
|
||||||
|
Profile profileFromQuery(const QSqlQuery& query)
|
||||||
|
{
|
||||||
|
Profile profile;
|
||||||
|
profile.id = query.value(0).toLongLong();
|
||||||
|
profile.name = query.value(1).toString();
|
||||||
|
profile.host = query.value(2).toString();
|
||||||
|
profile.port = query.value(3).toInt();
|
||||||
|
profile.username = query.value(4).toString();
|
||||||
|
profile.protocol = query.value(5).toString();
|
||||||
|
profile.authMode = query.value(6).toString();
|
||||||
|
profile.privateKeyPath = query.value(7).toString();
|
||||||
|
profile.knownHostsPolicy = query.value(8).toString();
|
||||||
|
if (profile.knownHostsPolicy.isEmpty()) {
|
||||||
|
profile.knownHostsPolicy = QStringLiteral("Ask");
|
||||||
|
}
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isProfileValid(const Profile& profile)
|
||||||
|
{
|
||||||
|
return !profile.name.trimmed().isEmpty() && !profile.host.trimmed().isEmpty()
|
||||||
|
&& profile.port >= 1 && profile.port <= 65535;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileRepository::ProfileRepository() : m_connectionName(QStringLiteral("orbithub_main"))
|
||||||
|
{
|
||||||
|
if (!initializeDatabase()) {
|
||||||
|
QSqlDatabase::removeDatabase(m_connectionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileRepository::~ProfileRepository()
|
||||||
|
{
|
||||||
|
if (QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
QSqlDatabase db = QSqlDatabase::database(m_connectionName);
|
||||||
|
if (db.isOpen()) {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QSqlDatabase::removeDatabase(m_connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProfileRepository::initError() const
|
||||||
|
{
|
||||||
|
return m_initError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProfileRepository::lastError() const
|
||||||
|
{
|
||||||
|
return m_lastError;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Profile> ProfileRepository::listProfiles(const QString& searchQuery) const
|
||||||
|
{
|
||||||
|
std::vector<Profile> result;
|
||||||
|
|
||||||
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastError(QString());
|
||||||
|
|
||||||
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
||||||
|
if (searchQuery.trimmed().isEmpty()) {
|
||||||
|
query.prepare(QStringLiteral(
|
||||||
|
"SELECT id, name, host, port, username, protocol, auth_mode, private_key_path, known_hosts_policy "
|
||||||
|
"FROM profiles "
|
||||||
|
"ORDER BY lower(name) ASC, id ASC"));
|
||||||
|
} else {
|
||||||
|
query.prepare(QStringLiteral(
|
||||||
|
"SELECT id, name, host, port, username, protocol, auth_mode, private_key_path, known_hosts_policy "
|
||||||
|
"FROM profiles "
|
||||||
|
"WHERE lower(name) LIKE lower(?) OR lower(host) LIKE lower(?) "
|
||||||
|
"ORDER BY lower(name) ASC, id ASC"));
|
||||||
|
const QString search = QStringLiteral("%") + searchQuery.trimmed() + QStringLiteral("%");
|
||||||
|
query.addBindValue(search);
|
||||||
|
query.addBindValue(search);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
setLastError(query.lastError().text());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (query.next()) {
|
||||||
|
result.push_back(profileFromQuery(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Profile> ProfileRepository::getProfile(qint64 id) const
|
||||||
|
{
|
||||||
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastError(QString());
|
||||||
|
|
||||||
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
||||||
|
query.prepare(QStringLiteral(
|
||||||
|
"SELECT id, name, host, port, username, protocol, auth_mode, private_key_path, known_hosts_policy "
|
||||||
|
"FROM profiles WHERE id = ?"));
|
||||||
|
query.addBindValue(id);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
setLastError(query.lastError().text());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query.next()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return profileFromQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Profile> ProfileRepository::createProfile(const Profile& profile) const
|
||||||
|
{
|
||||||
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastError(QString());
|
||||||
|
|
||||||
|
if (!isProfileValid(profile)) {
|
||||||
|
setLastError(QStringLiteral("Name, host, and a valid port are required."));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
||||||
|
query.prepare(QStringLiteral(
|
||||||
|
"INSERT INTO profiles(name, host, port, username, protocol, auth_mode, private_key_path, known_hosts_policy) "
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)"));
|
||||||
|
bindProfileFields(query, profile);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
setLastError(query.lastError().text());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Profile created = profile;
|
||||||
|
created.id = query.lastInsertId().toLongLong();
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProfileRepository::updateProfile(const Profile& profile) const
|
||||||
|
{
|
||||||
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastError(QString());
|
||||||
|
|
||||||
|
if (profile.id < 0 || !isProfileValid(profile)) {
|
||||||
|
setLastError(QStringLiteral("Invalid profile data."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
||||||
|
query.prepare(QStringLiteral(
|
||||||
|
"UPDATE profiles "
|
||||||
|
"SET name = ?, host = ?, port = ?, username = ?, protocol = ?, auth_mode = ?, private_key_path = ?, known_hosts_policy = ? "
|
||||||
|
"WHERE id = ?"));
|
||||||
|
bindProfileFields(query, profile);
|
||||||
|
query.addBindValue(profile.id);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
setLastError(query.lastError().text());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.numRowsAffected() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProfileRepository::deleteProfile(qint64 id) const
|
||||||
|
{
|
||||||
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastError(QString());
|
||||||
|
|
||||||
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
||||||
|
query.prepare(QStringLiteral("DELETE FROM profiles WHERE id = ?"));
|
||||||
|
query.addBindValue(id);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
setLastError(query.lastError().text());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.numRowsAffected() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProfileRepository::initializeDatabase()
|
||||||
|
{
|
||||||
|
QSqlDatabase database = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), m_connectionName);
|
||||||
|
database.setDatabaseName(buildDatabasePath());
|
||||||
|
|
||||||
|
if (!database.open()) {
|
||||||
|
m_initError = database.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query(database);
|
||||||
|
const bool created = query.exec(QStringLiteral(
|
||||||
|
"CREATE TABLE IF NOT EXISTS profiles ("
|
||||||
|
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||||
|
"name TEXT NOT NULL UNIQUE,"
|
||||||
|
"host TEXT NOT NULL DEFAULT '',"
|
||||||
|
"port INTEGER NOT NULL DEFAULT 22,"
|
||||||
|
"username TEXT NOT NULL DEFAULT '',"
|
||||||
|
"protocol TEXT NOT NULL DEFAULT 'SSH',"
|
||||||
|
"auth_mode TEXT NOT NULL DEFAULT 'Password',"
|
||||||
|
"private_key_path TEXT NOT NULL DEFAULT '',"
|
||||||
|
"known_hosts_policy TEXT NOT NULL DEFAULT 'Ask'"
|
||||||
|
")"));
|
||||||
|
|
||||||
|
if (!created) {
|
||||||
|
m_initError = query.lastError().text();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ensureProfileSchema()) {
|
||||||
|
m_initError = m_lastError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProfileRepository::ensureProfileSchema() const
|
||||||
|
{
|
||||||
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
||||||
|
setLastError(QStringLiteral("Database connection missing."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery tableInfo(QSqlDatabase::database(m_connectionName));
|
||||||
|
if (!tableInfo.exec(QStringLiteral("PRAGMA table_info(profiles)"))) {
|
||||||
|
setLastError(tableInfo.lastError().text());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> columns;
|
||||||
|
while (tableInfo.next()) {
|
||||||
|
columns.insert(tableInfo.value(1).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ColumnDef {
|
||||||
|
QString name;
|
||||||
|
QString ddl;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<ColumnDef> required = {
|
||||||
|
{QStringLiteral("host"), QStringLiteral("ALTER TABLE profiles ADD COLUMN host TEXT NOT NULL DEFAULT ''")},
|
||||||
|
{QStringLiteral("port"), QStringLiteral("ALTER TABLE profiles ADD COLUMN port INTEGER NOT NULL DEFAULT 22")},
|
||||||
|
{QStringLiteral("username"), QStringLiteral("ALTER TABLE profiles ADD COLUMN username TEXT NOT NULL DEFAULT ''")},
|
||||||
|
{QStringLiteral("protocol"), QStringLiteral("ALTER TABLE profiles ADD COLUMN protocol TEXT NOT NULL DEFAULT 'SSH'")},
|
||||||
|
{QStringLiteral("auth_mode"), QStringLiteral("ALTER TABLE profiles ADD COLUMN auth_mode TEXT NOT NULL DEFAULT 'Password'")},
|
||||||
|
{QStringLiteral("private_key_path"), QStringLiteral("ALTER TABLE profiles ADD COLUMN private_key_path TEXT NOT NULL DEFAULT ''")},
|
||||||
|
{QStringLiteral("known_hosts_policy"), QStringLiteral("ALTER TABLE profiles ADD COLUMN known_hosts_policy TEXT NOT NULL DEFAULT 'Ask'")}};
|
||||||
|
|
||||||
|
for (const ColumnDef& column : required) {
|
||||||
|
if (columns.contains(column.name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery alter(QSqlDatabase::database(m_connectionName));
|
||||||
|
if (!alter.exec(column.ddl)) {
|
||||||
|
setLastError(alter.lastError().text());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastError(QString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileRepository::setLastError(const QString& error) const
|
||||||
|
{
|
||||||
|
m_lastError = error;
|
||||||
|
}
|
||||||
48
src/profile_repository.h
Normal file
48
src/profile_repository.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef ORBITHUB_PROFILE_REPOSITORY_H
|
||||||
|
#define ORBITHUB_PROFILE_REPOSITORY_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct Profile
|
||||||
|
{
|
||||||
|
qint64 id = -1;
|
||||||
|
QString name;
|
||||||
|
QString host;
|
||||||
|
int port = 22;
|
||||||
|
QString username;
|
||||||
|
QString protocol = QStringLiteral("SSH");
|
||||||
|
QString authMode = QStringLiteral("Password");
|
||||||
|
QString privateKeyPath;
|
||||||
|
QString knownHostsPolicy = QStringLiteral("Ask");
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProfileRepository
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProfileRepository();
|
||||||
|
~ProfileRepository();
|
||||||
|
|
||||||
|
QString initError() const;
|
||||||
|
QString lastError() const;
|
||||||
|
|
||||||
|
std::vector<Profile> listProfiles(const QString& searchQuery = QString()) const;
|
||||||
|
std::optional<Profile> getProfile(qint64 id) const;
|
||||||
|
std::optional<Profile> createProfile(const Profile& profile) const;
|
||||||
|
bool updateProfile(const Profile& profile) const;
|
||||||
|
bool deleteProfile(qint64 id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_connectionName;
|
||||||
|
QString m_initError;
|
||||||
|
mutable QString m_lastError;
|
||||||
|
|
||||||
|
bool initializeDatabase();
|
||||||
|
bool ensureProfileSchema() const;
|
||||||
|
void setLastError(const QString& error) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "profiles_window.h"
|
#include "profiles_window.h"
|
||||||
|
|
||||||
|
#include "profile_dialog.h"
|
||||||
|
#include "profile_repository.h"
|
||||||
#include "session_window.h"
|
#include "session_window.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -12,25 +14,50 @@
|
|||||||
#include <QListWidgetItem>
|
#include <QListWidgetItem>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QStringList>
|
#include <QVariant>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QString formatProfileListItem(const Profile& profile)
|
||||||
|
{
|
||||||
|
return QStringLiteral("%1 [%2 %3:%4]")
|
||||||
|
.arg(profile.name, profile.protocol, profile.host, QString::number(profile.port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ProfilesWindow::ProfilesWindow(QWidget* parent)
|
ProfilesWindow::ProfilesWindow(QWidget* parent)
|
||||||
: QMainWindow(parent),
|
: QMainWindow(parent),
|
||||||
m_searchBox(nullptr),
|
m_searchBox(nullptr),
|
||||||
m_profilesList(nullptr),
|
m_profilesList(nullptr),
|
||||||
m_newButton(nullptr),
|
m_newButton(nullptr),
|
||||||
m_editButton(nullptr),
|
m_editButton(nullptr),
|
||||||
m_deleteButton(nullptr)
|
m_deleteButton(nullptr),
|
||||||
|
m_repository(std::make_unique<ProfileRepository>())
|
||||||
{
|
{
|
||||||
setWindowTitle(QStringLiteral("OrbitHub Profiles"));
|
setWindowTitle(QStringLiteral("OrbitHub Profiles"));
|
||||||
resize(520, 620);
|
resize(640, 620);
|
||||||
|
|
||||||
setupUi();
|
setupUi();
|
||||||
populateSampleProfiles();
|
|
||||||
|
if (!m_repository->initError().isEmpty()) {
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
QStringLiteral("Database Error"),
|
||||||
|
QStringLiteral("Failed to initialize SQLite database: %1")
|
||||||
|
.arg(m_repository->initError()));
|
||||||
|
m_newButton->setEnabled(false);
|
||||||
|
m_editButton->setEnabled(false);
|
||||||
|
m_deleteButton->setEnabled(false);
|
||||||
|
m_searchBox->setEnabled(false);
|
||||||
|
m_profilesList->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadProfiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProfilesWindow::~ProfilesWindow() = default;
|
||||||
|
|
||||||
void ProfilesWindow::setupUi()
|
void ProfilesWindow::setupUi()
|
||||||
{
|
{
|
||||||
auto* central = new QWidget(this);
|
auto* central = new QWidget(this);
|
||||||
@@ -38,7 +65,7 @@ void ProfilesWindow::setupUi()
|
|||||||
|
|
||||||
auto* searchLabel = new QLabel(QStringLiteral("Search"), central);
|
auto* searchLabel = new QLabel(QStringLiteral("Search"), central);
|
||||||
m_searchBox = new QLineEdit(central);
|
m_searchBox = new QLineEdit(central);
|
||||||
m_searchBox->setPlaceholderText(QStringLiteral("Filter profiles..."));
|
m_searchBox->setPlaceholderText(QStringLiteral("Filter by name or host..."));
|
||||||
|
|
||||||
m_profilesList = new QListWidget(central);
|
m_profilesList = new QListWidget(central);
|
||||||
m_profilesList->setSelectionMode(QAbstractItemView::SingleSelection);
|
m_profilesList->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
@@ -63,49 +90,155 @@ void ProfilesWindow::setupUi()
|
|||||||
connect(m_searchBox,
|
connect(m_searchBox,
|
||||||
&QLineEdit::textChanged,
|
&QLineEdit::textChanged,
|
||||||
this,
|
this,
|
||||||
[this](const QString& text) { filterProfiles(text); });
|
[this](const QString& text) { loadProfiles(text); });
|
||||||
|
|
||||||
connect(m_profilesList,
|
connect(m_profilesList,
|
||||||
&QListWidget::itemDoubleClicked,
|
&QListWidget::itemDoubleClicked,
|
||||||
this,
|
this,
|
||||||
[this](QListWidgetItem* item) { openSessionForItem(item); });
|
[this](QListWidgetItem* item) { openSessionForItem(item); });
|
||||||
|
|
||||||
// Milestone 0 keeps profile management as placeholders.
|
connect(m_newButton, &QPushButton::clicked, this, [this]() { createProfile(); });
|
||||||
auto showTodo = [this](const QString& action) {
|
connect(m_editButton, &QPushButton::clicked, this, [this]() { editSelectedProfile(); });
|
||||||
QMessageBox::information(this,
|
connect(m_deleteButton, &QPushButton::clicked, this, [this]() { deleteSelectedProfile(); });
|
||||||
QStringLiteral("Milestone 0"),
|
|
||||||
QStringLiteral("%1 is planned for Milestone 1.").arg(action));
|
|
||||||
};
|
|
||||||
|
|
||||||
connect(m_newButton, &QPushButton::clicked, this, [showTodo]() { showTodo(QStringLiteral("New Profile")); });
|
|
||||||
connect(m_editButton, &QPushButton::clicked, this, [showTodo]() { showTodo(QStringLiteral("Edit Profile")); });
|
|
||||||
connect(m_deleteButton,
|
|
||||||
&QPushButton::clicked,
|
|
||||||
this,
|
|
||||||
[showTodo]() { showTodo(QStringLiteral("Delete Profile")); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfilesWindow::populateSampleProfiles()
|
void ProfilesWindow::loadProfiles(const QString& query)
|
||||||
{
|
{
|
||||||
const QStringList sampleProfiles = {
|
m_profilesList->clear();
|
||||||
QStringLiteral("Production Bastion"),
|
m_profileCache.clear();
|
||||||
QStringLiteral("Staging API Host"),
|
|
||||||
QStringLiteral("Local Lab VM"),
|
|
||||||
QStringLiteral("CI Build Agent")};
|
|
||||||
|
|
||||||
m_profilesList->addItems(sampleProfiles);
|
const std::vector<Profile> profiles = m_repository->listProfiles(query);
|
||||||
}
|
if (!m_repository->lastError().isEmpty()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
void ProfilesWindow::filterProfiles(const QString& query)
|
QStringLiteral("Load Profiles"),
|
||||||
{
|
QStringLiteral("Failed to load profiles: %1")
|
||||||
const QString trimmed = query.trimmed();
|
.arg(m_repository->lastError()));
|
||||||
|
return;
|
||||||
for (int i = 0; i < m_profilesList->count(); ++i) {
|
|
||||||
QListWidgetItem* item = m_profilesList->item(i);
|
|
||||||
const bool matches = trimmed.isEmpty()
|
|
||||||
|| item->text().contains(trimmed, Qt::CaseInsensitive);
|
|
||||||
item->setHidden(!matches);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const Profile& profile : profiles) {
|
||||||
|
auto* item = new QListWidgetItem(formatProfileListItem(profile), m_profilesList);
|
||||||
|
item->setData(Qt::UserRole, QVariant::fromValue(profile.id));
|
||||||
|
item->setToolTip(QStringLiteral("%1://%2@%3:%4\nAuth: %5\nKnown Hosts: %6")
|
||||||
|
.arg(profile.protocol,
|
||||||
|
profile.username.isEmpty() ? QStringLiteral("<none>") : profile.username,
|
||||||
|
profile.host,
|
||||||
|
QString::number(profile.port),
|
||||||
|
profile.authMode,
|
||||||
|
profile.knownHostsPolicy));
|
||||||
|
m_profileCache.insert_or_assign(profile.id, profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Profile> ProfilesWindow::selectedProfile() const
|
||||||
|
{
|
||||||
|
QListWidgetItem* item = m_profilesList->currentItem();
|
||||||
|
if (item == nullptr) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVariant value = item->data(Qt::UserRole);
|
||||||
|
if (!value.isValid()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const qint64 id = value.toLongLong();
|
||||||
|
const auto cacheIt = m_profileCache.find(id);
|
||||||
|
if (cacheIt != m_profileCache.end()) {
|
||||||
|
return cacheIt->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_repository->getProfile(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilesWindow::createProfile()
|
||||||
|
{
|
||||||
|
ProfileDialog dialog(this);
|
||||||
|
dialog.setDialogTitle(QStringLiteral("New Profile"));
|
||||||
|
|
||||||
|
if (dialog.exec() != QDialog::Accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_repository->createProfile(dialog.profile()).has_value()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Create Profile"),
|
||||||
|
QStringLiteral("Failed to create profile: %1")
|
||||||
|
.arg(m_repository->lastError().isEmpty()
|
||||||
|
? QStringLiteral("unknown error")
|
||||||
|
: m_repository->lastError()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadProfiles(m_searchBox->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilesWindow::editSelectedProfile()
|
||||||
|
{
|
||||||
|
const std::optional<Profile> selected = selectedProfile();
|
||||||
|
if (!selected.has_value()) {
|
||||||
|
QMessageBox::information(this,
|
||||||
|
QStringLiteral("Edit Profile"),
|
||||||
|
QStringLiteral("Select a profile first."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileDialog dialog(this);
|
||||||
|
dialog.setDialogTitle(QStringLiteral("Edit Profile"));
|
||||||
|
dialog.setProfile(selected.value());
|
||||||
|
|
||||||
|
if (dialog.exec() != QDialog::Accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Profile updated = dialog.profile();
|
||||||
|
updated.id = selected->id;
|
||||||
|
|
||||||
|
if (!m_repository->updateProfile(updated)) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Edit Profile"),
|
||||||
|
QStringLiteral("Failed to update profile: %1")
|
||||||
|
.arg(m_repository->lastError().isEmpty()
|
||||||
|
? QStringLiteral("unknown error")
|
||||||
|
: m_repository->lastError()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadProfiles(m_searchBox->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilesWindow::deleteSelectedProfile()
|
||||||
|
{
|
||||||
|
const std::optional<Profile> selected = selectedProfile();
|
||||||
|
if (!selected.has_value()) {
|
||||||
|
QMessageBox::information(this,
|
||||||
|
QStringLiteral("Delete Profile"),
|
||||||
|
QStringLiteral("Select a profile first."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QMessageBox::StandardButton confirm = QMessageBox::question(
|
||||||
|
this,
|
||||||
|
QStringLiteral("Delete Profile"),
|
||||||
|
QStringLiteral("Delete profile '%1'?").arg(selected->name),
|
||||||
|
QMessageBox::Yes | QMessageBox::No,
|
||||||
|
QMessageBox::No);
|
||||||
|
|
||||||
|
if (confirm != QMessageBox::Yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_repository->deleteProfile(selected->id)) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Delete Profile"),
|
||||||
|
QStringLiteral("Failed to delete profile: %1")
|
||||||
|
.arg(m_repository->lastError().isEmpty()
|
||||||
|
? QStringLiteral("unknown error")
|
||||||
|
: m_repository->lastError()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadProfiles(m_searchBox->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfilesWindow::openSessionForItem(QListWidgetItem* item)
|
void ProfilesWindow::openSessionForItem(QListWidgetItem* item)
|
||||||
@@ -114,7 +247,24 @@ void ProfilesWindow::openSessionForItem(QListWidgetItem* item)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* session = new SessionWindow(item->text());
|
const QVariant value = item->data(Qt::UserRole);
|
||||||
|
if (!value.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const qint64 id = value.toLongLong();
|
||||||
|
const std::optional<Profile> profile = m_repository->getProfile(id);
|
||||||
|
if (!profile.has_value()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Connect"),
|
||||||
|
QStringLiteral("Failed to load profile for session: %1")
|
||||||
|
.arg(m_repository->lastError().isEmpty()
|
||||||
|
? QStringLiteral("profile not found")
|
||||||
|
: m_repository->lastError()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* session = new SessionWindow(profile.value());
|
||||||
session->setAttribute(Qt::WA_DeleteOnClose);
|
session->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
m_sessionWindows.emplace_back(session);
|
m_sessionWindows.emplace_back(session);
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
#ifndef ORBITHUB_PROFILES_WINDOW_H
|
#ifndef ORBITHUB_PROFILES_WINDOW_H
|
||||||
#define ORBITHUB_PROFILES_WINDOW_H
|
#define ORBITHUB_PROFILES_WINDOW_H
|
||||||
|
|
||||||
|
#include "profile_repository.h"
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class QListWidget;
|
class QListWidget;
|
||||||
@@ -19,6 +25,7 @@ class ProfilesWindow : public QMainWindow
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ProfilesWindow(QWidget* parent = nullptr);
|
explicit ProfilesWindow(QWidget* parent = nullptr);
|
||||||
|
~ProfilesWindow() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLineEdit* m_searchBox;
|
QLineEdit* m_searchBox;
|
||||||
@@ -27,10 +34,15 @@ private:
|
|||||||
QPushButton* m_editButton;
|
QPushButton* m_editButton;
|
||||||
QPushButton* m_deleteButton;
|
QPushButton* m_deleteButton;
|
||||||
std::vector<QPointer<SessionWindow>> m_sessionWindows;
|
std::vector<QPointer<SessionWindow>> m_sessionWindows;
|
||||||
|
std::unique_ptr<ProfileRepository> m_repository;
|
||||||
|
std::unordered_map<qint64, Profile> m_profileCache;
|
||||||
|
|
||||||
void setupUi();
|
void setupUi();
|
||||||
void populateSampleProfiles();
|
void loadProfiles(const QString& query = QString());
|
||||||
void filterProfiles(const QString& query);
|
std::optional<Profile> selectedProfile() const;
|
||||||
|
void createProfile();
|
||||||
|
void editSelectedProfile();
|
||||||
|
void deleteSelectedProfile();
|
||||||
void openSessionForItem(QListWidgetItem* item);
|
void openSessionForItem(QListWidgetItem* item);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
62
src/session_backend.h
Normal file
62
src/session_backend.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#ifndef ORBITHUB_SESSION_BACKEND_H
|
||||||
|
#define ORBITHUB_SESSION_BACKEND_H
|
||||||
|
|
||||||
|
#include "profile_repository.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class SessionConnectOptions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString password;
|
||||||
|
QString privateKeyPath;
|
||||||
|
QString knownHostsPolicy;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SessionState {
|
||||||
|
Disconnected,
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
Failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
class SessionBackend : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SessionBackend(const Profile& profile, QObject* parent = nullptr)
|
||||||
|
: QObject(parent), m_profile(profile)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~SessionBackend() override = default;
|
||||||
|
|
||||||
|
const Profile& profile() const
|
||||||
|
{
|
||||||
|
return m_profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
virtual void connectSession(const SessionConnectOptions& options) = 0;
|
||||||
|
virtual void disconnectSession() = 0;
|
||||||
|
virtual void reconnectSession(const SessionConnectOptions& options) = 0;
|
||||||
|
virtual void sendInput(const QString& input) = 0;
|
||||||
|
virtual void confirmHostKey(bool trustHost) = 0;
|
||||||
|
virtual void updateTerminalSize(int columns, int rows) = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void stateChanged(SessionState state, const QString& message);
|
||||||
|
void eventLogged(const QString& message);
|
||||||
|
void connectionError(const QString& displayMessage, const QString& rawMessage);
|
||||||
|
void outputReceived(const QString& text);
|
||||||
|
void hostKeyConfirmationRequested(const QString& prompt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Profile m_profile;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(SessionConnectOptions)
|
||||||
|
Q_DECLARE_METATYPE(SessionState)
|
||||||
|
|
||||||
|
#endif
|
||||||
14
src/session_backend_factory.cpp
Normal file
14
src/session_backend_factory.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "session_backend_factory.h"
|
||||||
|
|
||||||
|
#include "session_backend.h"
|
||||||
|
#include "ssh_session_backend.h"
|
||||||
|
#include "unsupported_session_backend.h"
|
||||||
|
|
||||||
|
std::unique_ptr<SessionBackend> createSessionBackend(const Profile& profile)
|
||||||
|
{
|
||||||
|
if (profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) == 0) {
|
||||||
|
return std::make_unique<SshSessionBackend>(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<UnsupportedSessionBackend>(profile);
|
||||||
|
}
|
||||||
12
src/session_backend_factory.h
Normal file
12
src/session_backend_factory.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef ORBITHUB_SESSION_BACKEND_FACTORY_H
|
||||||
|
#define ORBITHUB_SESSION_BACKEND_FACTORY_H
|
||||||
|
|
||||||
|
#include "profile_repository.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class SessionBackend;
|
||||||
|
|
||||||
|
std::unique_ptr<SessionBackend> createSessionBackend(const Profile& profile);
|
||||||
|
|
||||||
|
#endif
|
||||||
670
src/session_tab.cpp
Normal file
670
src/session_tab.cpp
Normal file
@@ -0,0 +1,670 @@
|
|||||||
|
#include "session_tab.h"
|
||||||
|
|
||||||
|
#include "session_backend_factory.h"
|
||||||
|
#include "terminal_view.h"
|
||||||
|
|
||||||
|
#include <KodoTerm/KodoTerm.hpp>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QFont defaultTerminalFont()
|
||||||
|
{
|
||||||
|
QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||||
|
font.setStyleHint(QFont::Monospace);
|
||||||
|
font.setFixedPitch(true);
|
||||||
|
font.setKerning(false);
|
||||||
|
font.setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalTheme themeForName(const QString& themeName)
|
||||||
|
{
|
||||||
|
if (themeName.compare(QStringLiteral("Light"), Qt::CaseInsensitive) == 0) {
|
||||||
|
return TerminalTheme::loadKonsoleTheme(
|
||||||
|
QStringLiteral(":/KodoTermThemes/konsole/BlackOnWhite.colorscheme"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themeName.compare(QStringLiteral("Solarized Dark"), Qt::CaseInsensitive) == 0) {
|
||||||
|
return TerminalTheme::loadKonsoleTheme(
|
||||||
|
QStringLiteral(":/KodoTermThemes/konsole/Solarized.colorscheme"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TerminalTheme::loadKonsoleTheme(
|
||||||
|
QStringLiteral(":/KodoTermThemes/konsole/Breeze.colorscheme"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionTab::SessionTab(const Profile& profile, QWidget* parent)
|
||||||
|
: QWidget(parent),
|
||||||
|
m_profile(profile),
|
||||||
|
m_backendThread(nullptr),
|
||||||
|
m_backend(nullptr),
|
||||||
|
m_useKodoTermForSsh(profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive)
|
||||||
|
== 0),
|
||||||
|
m_state(SessionState::Disconnected),
|
||||||
|
m_terminalThemeName(QStringLiteral("Dark")),
|
||||||
|
m_sshTerminal(nullptr),
|
||||||
|
m_terminalOutput(nullptr),
|
||||||
|
m_eventLog(nullptr),
|
||||||
|
m_toggleEventsButton(nullptr),
|
||||||
|
m_eventsPanel(nullptr)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<SessionConnectOptions>("SessionConnectOptions");
|
||||||
|
qRegisterMetaType<SessionState>("SessionState");
|
||||||
|
|
||||||
|
setupUi();
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
connect(m_sshTerminal,
|
||||||
|
&KodoTerm::finished,
|
||||||
|
this,
|
||||||
|
[this](int exitCode, int) {
|
||||||
|
if (m_state == SessionState::Disconnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state == SessionState::Connected) {
|
||||||
|
if (exitCode != 0) {
|
||||||
|
appendEvent(QStringLiteral("SSH session closed with exit code %1.")
|
||||||
|
.arg(exitCode));
|
||||||
|
}
|
||||||
|
setState(SessionState::Disconnected,
|
||||||
|
QStringLiteral("SSH session closed."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exitCode == 0) {
|
||||||
|
setState(SessionState::Disconnected,
|
||||||
|
QStringLiteral("SSH session ended."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lastError = QStringLiteral("ssh exited with code %1").arg(exitCode);
|
||||||
|
appendEvent(QStringLiteral("Error: %1").arg(m_lastError));
|
||||||
|
setState(SessionState::Failed,
|
||||||
|
QStringLiteral("SSH session exited unexpectedly."));
|
||||||
|
});
|
||||||
|
connect(m_sshTerminal,
|
||||||
|
&KodoTerm::cwdChanged,
|
||||||
|
this,
|
||||||
|
[this](const QString& cwd) {
|
||||||
|
if (!cwd.trimmed().isEmpty()) {
|
||||||
|
appendEvent(QStringLiteral("Remote cwd: %1").arg(cwd));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
m_backendThread = new QThread(this);
|
||||||
|
|
||||||
|
std::unique_ptr<SessionBackend> backend = createSessionBackend(m_profile);
|
||||||
|
m_backend = backend.release();
|
||||||
|
m_backend->moveToThread(m_backendThread);
|
||||||
|
|
||||||
|
connect(m_backendThread, &QThread::finished, m_backend, &QObject::deleteLater);
|
||||||
|
connect(this,
|
||||||
|
&SessionTab::requestConnect,
|
||||||
|
m_backend,
|
||||||
|
&SessionBackend::connectSession,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(this,
|
||||||
|
&SessionTab::requestDisconnect,
|
||||||
|
m_backend,
|
||||||
|
&SessionBackend::disconnectSession,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(this,
|
||||||
|
&SessionTab::requestReconnect,
|
||||||
|
m_backend,
|
||||||
|
&SessionBackend::reconnectSession,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(this,
|
||||||
|
&SessionTab::requestInput,
|
||||||
|
m_backend,
|
||||||
|
&SessionBackend::sendInput,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(this,
|
||||||
|
&SessionTab::requestHostKeyConfirmation,
|
||||||
|
m_backend,
|
||||||
|
&SessionBackend::confirmHostKey,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(this,
|
||||||
|
&SessionTab::requestTerminalSize,
|
||||||
|
m_backend,
|
||||||
|
&SessionBackend::updateTerminalSize,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(m_backend,
|
||||||
|
&SessionBackend::stateChanged,
|
||||||
|
this,
|
||||||
|
&SessionTab::onBackendStateChanged,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(m_backend,
|
||||||
|
&SessionBackend::eventLogged,
|
||||||
|
this,
|
||||||
|
&SessionTab::onBackendEventLogged,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(m_backend,
|
||||||
|
&SessionBackend::connectionError,
|
||||||
|
this,
|
||||||
|
&SessionTab::onBackendConnectionError,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(m_backend,
|
||||||
|
&SessionBackend::outputReceived,
|
||||||
|
this,
|
||||||
|
&SessionTab::onBackendOutputReceived,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
connect(m_backend,
|
||||||
|
&SessionBackend::hostKeyConfirmationRequested,
|
||||||
|
this,
|
||||||
|
&SessionTab::onBackendHostKeyConfirmationRequested,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
|
m_backendThread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(SessionState::Disconnected, QStringLiteral("Ready to connect."));
|
||||||
|
QTimer::singleShot(0, this, &SessionTab::connectSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionTab::~SessionTab()
|
||||||
|
{
|
||||||
|
if (m_useKodoTermForSsh && m_sshTerminal != nullptr && m_state != SessionState::Disconnected) {
|
||||||
|
m_sshTerminal->kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_backend != nullptr && m_backendThread != nullptr && m_backendThread->isRunning()) {
|
||||||
|
QMetaObject::invokeMethod(m_backend, "disconnectSession", Qt::BlockingQueuedConnection);
|
||||||
|
m_backendThread->quit();
|
||||||
|
m_backendThread->wait(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SessionTab::tabTitle() const
|
||||||
|
{
|
||||||
|
return QStringLiteral("%1 (%2)").arg(m_profile.name, stateSuffix());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::connectSession()
|
||||||
|
{
|
||||||
|
if (m_state == SessionState::Connecting || m_state == SessionState::Connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validateProfileForConnect()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<SessionConnectOptions> options = buildConnectOptions();
|
||||||
|
if (!options.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lastConnectOptions = options.value();
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
if (!startSshTerminal(options.value())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit requestConnect(options.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::disconnectSession()
|
||||||
|
{
|
||||||
|
if (m_state == SessionState::Disconnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
if (m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->kill();
|
||||||
|
}
|
||||||
|
setState(SessionState::Disconnected, QStringLiteral("Session disconnected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit requestDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::reconnectSession()
|
||||||
|
{
|
||||||
|
if (!validateProfileForConnect()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<SessionConnectOptions> options = buildConnectOptions();
|
||||||
|
if (!options.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lastConnectOptions = options.value();
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
if (m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->kill();
|
||||||
|
}
|
||||||
|
QTimer::singleShot(50,
|
||||||
|
this,
|
||||||
|
[this, options]() { startSshTerminal(options.value()); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit requestReconnect(options.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::clearTerminal()
|
||||||
|
{
|
||||||
|
if (m_useKodoTermForSsh && m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->clearScrollback();
|
||||||
|
m_sshTerminal->setFocus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_terminalOutput != nullptr) {
|
||||||
|
m_terminalOutput->clear();
|
||||||
|
if (m_state == SessionState::Connected) {
|
||||||
|
emit requestInput(QStringLiteral("\x0c"));
|
||||||
|
}
|
||||||
|
m_terminalOutput->setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::setTerminalThemeName(const QString& themeName)
|
||||||
|
{
|
||||||
|
const QString normalized = themeName.trimmed();
|
||||||
|
if (normalized.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_terminalThemeName.compare(normalized, Qt::CaseInsensitive) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_terminalThemeName = normalized;
|
||||||
|
applyTerminalTheme(m_terminalThemeName);
|
||||||
|
appendEvent(QStringLiteral("Terminal theme set to %1.").arg(m_terminalThemeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SessionTab::terminalThemeName() const
|
||||||
|
{
|
||||||
|
return m_terminalThemeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::onBackendStateChanged(SessionState state, const QString& message)
|
||||||
|
{
|
||||||
|
setState(state, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::onBackendEventLogged(const QString& message)
|
||||||
|
{
|
||||||
|
appendEvent(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::onBackendConnectionError(const QString& displayMessage, const QString& rawMessage)
|
||||||
|
{
|
||||||
|
m_lastError = rawMessage.isEmpty() ? displayMessage : rawMessage;
|
||||||
|
appendEvent(QStringLiteral("Error: %1").arg(displayMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::onBackendOutputReceived(const QString& text)
|
||||||
|
{
|
||||||
|
if (text.isEmpty() || m_terminalOutput == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_terminalOutput->appendTerminalData(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::onBackendHostKeyConfirmationRequested(const QString& prompt)
|
||||||
|
{
|
||||||
|
const QString question = prompt.isEmpty()
|
||||||
|
? QStringLiteral("Unknown SSH host key. Do you trust this host?")
|
||||||
|
: prompt;
|
||||||
|
|
||||||
|
const QMessageBox::StandardButton reply = QMessageBox::question(
|
||||||
|
this,
|
||||||
|
QStringLiteral("SSH Host Key Confirmation"),
|
||||||
|
QStringLiteral("%1\n\nTrust and continue?").arg(question),
|
||||||
|
QMessageBox::Yes | QMessageBox::No,
|
||||||
|
QMessageBox::No);
|
||||||
|
|
||||||
|
emit requestHostKeyConfirmation(reply == QMessageBox::Yes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::setupUi()
|
||||||
|
{
|
||||||
|
auto* rootLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
m_sshTerminal = new KodoTerm(this);
|
||||||
|
const QFont terminalFont = defaultTerminalFont();
|
||||||
|
|
||||||
|
KodoTermConfig config = m_sshTerminal->getConfig();
|
||||||
|
config.font = terminalFont;
|
||||||
|
config.textAntialiasing = true;
|
||||||
|
config.maxScrollback = 12000;
|
||||||
|
m_sshTerminal->setConfig(config);
|
||||||
|
rootLayout->addWidget(m_sshTerminal, 1);
|
||||||
|
} else {
|
||||||
|
m_terminalOutput = new TerminalView(this);
|
||||||
|
m_terminalOutput->setFont(defaultTerminalFont());
|
||||||
|
m_terminalOutput->setMinimumHeight(260);
|
||||||
|
m_terminalOutput->setPlaceholderText(
|
||||||
|
QStringLiteral("Session is connecting. Type directly here once connected."));
|
||||||
|
rootLayout->addWidget(m_terminalOutput, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTerminalTheme(m_terminalThemeName);
|
||||||
|
|
||||||
|
auto* eventsHeader = new QHBoxLayout();
|
||||||
|
m_toggleEventsButton = new QToolButton(this);
|
||||||
|
m_toggleEventsButton->setCheckable(true);
|
||||||
|
eventsHeader->addWidget(m_toggleEventsButton);
|
||||||
|
eventsHeader->addStretch();
|
||||||
|
|
||||||
|
m_eventsPanel = new QWidget(this);
|
||||||
|
auto* eventsLayout = new QVBoxLayout(m_eventsPanel);
|
||||||
|
eventsLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto* eventTitle = new QLabel(QStringLiteral("Session Events"), m_eventsPanel);
|
||||||
|
m_eventLog = new QPlainTextEdit(m_eventsPanel);
|
||||||
|
m_eventLog->setReadOnly(true);
|
||||||
|
m_eventLog->setPlaceholderText(QStringLiteral("Session event log..."));
|
||||||
|
m_eventLog->setMinimumHeight(140);
|
||||||
|
|
||||||
|
eventsLayout->addWidget(eventTitle);
|
||||||
|
eventsLayout->addWidget(m_eventLog);
|
||||||
|
|
||||||
|
rootLayout->addLayout(eventsHeader);
|
||||||
|
rootLayout->addWidget(m_eventsPanel);
|
||||||
|
|
||||||
|
setPanelExpanded(m_toggleEventsButton, m_eventsPanel, QStringLiteral("Events"), false);
|
||||||
|
|
||||||
|
connect(m_toggleEventsButton,
|
||||||
|
&QToolButton::toggled,
|
||||||
|
this,
|
||||||
|
[this](bool expanded) {
|
||||||
|
setPanelExpanded(
|
||||||
|
m_toggleEventsButton, m_eventsPanel, QStringLiteral("Events"), expanded);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_terminalOutput != nullptr) {
|
||||||
|
connect(m_terminalOutput,
|
||||||
|
&TerminalView::inputGenerated,
|
||||||
|
this,
|
||||||
|
[this](const QString& input) { emit requestInput(input); });
|
||||||
|
connect(m_terminalOutput,
|
||||||
|
&TerminalView::terminalSizeChanged,
|
||||||
|
this,
|
||||||
|
[this](int columns, int rows) { emit requestTerminalSize(columns, rows); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
||||||
|
{
|
||||||
|
SessionConnectOptions options;
|
||||||
|
options.knownHostsPolicy = m_profile.knownHostsPolicy;
|
||||||
|
|
||||||
|
if (m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) != 0) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh
|
||||||
|
&& m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0) {
|
||||||
|
// Password is entered directly in terminal prompt.
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0) {
|
||||||
|
bool accepted = false;
|
||||||
|
const QString password = QInputDialog::getText(this,
|
||||||
|
QStringLiteral("SSH Password"),
|
||||||
|
QStringLiteral("Password for %1@%2:")
|
||||||
|
.arg(m_profile.username, m_profile.host),
|
||||||
|
QLineEdit::Password,
|
||||||
|
QString(),
|
||||||
|
&accepted);
|
||||||
|
if (!accepted) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.isEmpty()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Connect"),
|
||||||
|
QStringLiteral("Password is required for password authentication."));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.password = password;
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString keyPath = m_profile.privateKeyPath.trimmed();
|
||||||
|
if (keyPath.isEmpty()) {
|
||||||
|
keyPath = QFileDialog::getOpenFileName(this,
|
||||||
|
QStringLiteral("Select Private Key"),
|
||||||
|
QString(),
|
||||||
|
QStringLiteral("All Files (*)"));
|
||||||
|
if (keyPath.isEmpty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QFileInfo::exists(keyPath)) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Connect"),
|
||||||
|
QStringLiteral("Private key file not found: %1").arg(keyPath));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.privateKeyPath = keyPath;
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SessionTab::validateProfileForConnect()
|
||||||
|
{
|
||||||
|
if (m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_profile.host.trimmed().isEmpty()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Connect"),
|
||||||
|
QStringLiteral("SSH host is required."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_profile.username.trimmed().isEmpty()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Connect"),
|
||||||
|
QStringLiteral("SSH username is required."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_profile.port < 1 || m_profile.port > 65535) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Connect"),
|
||||||
|
QStringLiteral("SSH port must be between 1 and 65535."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::appendEvent(const QString& message)
|
||||||
|
{
|
||||||
|
const QString timestamp = QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
m_eventLog->appendPlainText(QStringLiteral("[%1] %2").arg(timestamp, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::setState(SessionState state, const QString& message)
|
||||||
|
{
|
||||||
|
m_state = state;
|
||||||
|
appendEvent(QStringLiteral("Connection state: %1").arg(message));
|
||||||
|
|
||||||
|
refreshActionButtons();
|
||||||
|
emit tabTitleChanged(tabTitle());
|
||||||
|
emit tabStateChanged(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SessionTab::stateSuffix() const
|
||||||
|
{
|
||||||
|
switch (m_state) {
|
||||||
|
case SessionState::Disconnected:
|
||||||
|
return QStringLiteral("Disconnected");
|
||||||
|
case SessionState::Connecting:
|
||||||
|
return QStringLiteral("Connecting");
|
||||||
|
case SessionState::Connected:
|
||||||
|
return QStringLiteral("Connected");
|
||||||
|
case SessionState::Failed:
|
||||||
|
return QStringLiteral("Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QStringLiteral("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::refreshActionButtons()
|
||||||
|
{
|
||||||
|
const bool isConnected = m_state == SessionState::Connected;
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh && m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->setEnabled(true);
|
||||||
|
m_sshTerminal->setFocus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_terminalOutput != nullptr) {
|
||||||
|
m_terminalOutput->setEnabled(isConnected);
|
||||||
|
m_terminalOutput->setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::setPanelExpanded(QToolButton* button,
|
||||||
|
QWidget* panel,
|
||||||
|
const QString& name,
|
||||||
|
bool expanded)
|
||||||
|
{
|
||||||
|
if (button == nullptr || panel == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
button->blockSignals(true);
|
||||||
|
button->setChecked(expanded);
|
||||||
|
button->blockSignals(false);
|
||||||
|
|
||||||
|
panel->setVisible(expanded);
|
||||||
|
button->setText(expanded ? QStringLiteral("Hide %1").arg(name)
|
||||||
|
: QStringLiteral("Show %1").arg(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SessionTab::startSshTerminal(const SessionConnectOptions& options)
|
||||||
|
{
|
||||||
|
if (m_sshTerminal == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList args;
|
||||||
|
args << QStringLiteral("-tt") << QStringLiteral("-p") << QString::number(m_profile.port)
|
||||||
|
<< QStringLiteral("-o") << QStringLiteral("ConnectTimeout=12") << QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("ServerAliveInterval=20") << QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("ServerAliveCountMax=2");
|
||||||
|
|
||||||
|
const QString policy = options.knownHostsPolicy.trimmed().isEmpty()
|
||||||
|
? m_profile.knownHostsPolicy.trimmed()
|
||||||
|
: options.knownHostsPolicy.trimmed();
|
||||||
|
|
||||||
|
if (policy.compare(QStringLiteral("Ignore"), Qt::CaseInsensitive) == 0) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
const QString knownHostsNullDevice = QStringLiteral("NUL");
|
||||||
|
#else
|
||||||
|
const QString knownHostsNullDevice = QStringLiteral("/dev/null");
|
||||||
|
#endif
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=no")
|
||||||
|
<< QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("UserKnownHostsFile=%1").arg(knownHostsNullDevice);
|
||||||
|
} else if (policy.compare(QStringLiteral("Accept New"), Qt::CaseInsensitive) == 0) {
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=accept-new");
|
||||||
|
} else if (policy.compare(QStringLiteral("Ask"), Qt::CaseInsensitive) == 0) {
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=ask");
|
||||||
|
} else {
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=yes");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_profile.authMode.compare(QStringLiteral("Private Key"), Qt::CaseInsensitive) == 0) {
|
||||||
|
QString keyPath = options.privateKeyPath.trimmed();
|
||||||
|
if (keyPath.isEmpty()) {
|
||||||
|
keyPath = m_profile.privateKeyPath.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyPath.isEmpty()) {
|
||||||
|
m_lastError = QStringLiteral("Private key path is required.");
|
||||||
|
appendEvent(QStringLiteral("Error: %1").arg(m_lastError));
|
||||||
|
setState(SessionState::Failed, m_lastError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
args << QStringLiteral("-i") << keyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString target = m_profile.username.trimmed().isEmpty()
|
||||||
|
? m_profile.host.trimmed()
|
||||||
|
: QStringLiteral("%1@%2").arg(m_profile.username.trimmed(), m_profile.host.trimmed());
|
||||||
|
args << target;
|
||||||
|
|
||||||
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||||
|
if (!env.contains(QStringLiteral("TERM"))) {
|
||||||
|
env.insert(QStringLiteral("TERM"), QStringLiteral("xterm-256color"));
|
||||||
|
}
|
||||||
|
if (!env.contains(QStringLiteral("COLORTERM"))) {
|
||||||
|
env.insert(QStringLiteral("COLORTERM"), QStringLiteral("truecolor"));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sshTerminal->setProgram(QStringLiteral("ssh"));
|
||||||
|
m_sshTerminal->setArguments(args);
|
||||||
|
m_sshTerminal->setProcessEnvironment(env);
|
||||||
|
|
||||||
|
appendEvent(QStringLiteral("Launching SSH terminal session."));
|
||||||
|
setState(SessionState::Connecting, QStringLiteral("Starting SSH terminal..."));
|
||||||
|
|
||||||
|
if (!m_sshTerminal->start()) {
|
||||||
|
m_lastError = QStringLiteral("Failed to start embedded SSH terminal process.");
|
||||||
|
appendEvent(QStringLiteral("Error: %1").arg(m_lastError));
|
||||||
|
setState(SessionState::Failed, QStringLiteral("Failed to start SSH terminal."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(SessionState::Connected, QStringLiteral("SSH session established."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::applyTerminalTheme(const QString& themeName)
|
||||||
|
{
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
if (m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->setTheme(themeForName(themeName));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_terminalOutput != nullptr) {
|
||||||
|
m_terminalOutput->setThemeName(themeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/session_tab.h
Normal file
79
src/session_tab.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#ifndef ORBITHUB_SESSION_TAB_H
|
||||||
|
#define ORBITHUB_SESSION_TAB_H
|
||||||
|
|
||||||
|
#include "profile_repository.h"
|
||||||
|
#include "session_backend.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class QPlainTextEdit;
|
||||||
|
class QThread;
|
||||||
|
class SessionBackend;
|
||||||
|
class TerminalView;
|
||||||
|
class QToolButton;
|
||||||
|
class KodoTerm;
|
||||||
|
|
||||||
|
class SessionTab : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SessionTab(const Profile& profile, QWidget* parent = nullptr);
|
||||||
|
~SessionTab() override;
|
||||||
|
|
||||||
|
QString tabTitle() const;
|
||||||
|
void connectSession();
|
||||||
|
void disconnectSession();
|
||||||
|
void reconnectSession();
|
||||||
|
void clearTerminal();
|
||||||
|
void setTerminalThemeName(const QString& themeName);
|
||||||
|
QString terminalThemeName() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void tabTitleChanged(const QString& title);
|
||||||
|
void tabStateChanged(SessionState state);
|
||||||
|
void requestConnect(const SessionConnectOptions& options);
|
||||||
|
void requestDisconnect();
|
||||||
|
void requestReconnect(const SessionConnectOptions& options);
|
||||||
|
void requestInput(const QString& input);
|
||||||
|
void requestHostKeyConfirmation(bool trustHost);
|
||||||
|
void requestTerminalSize(int columns, int rows);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onBackendStateChanged(SessionState state, const QString& message);
|
||||||
|
void onBackendEventLogged(const QString& message);
|
||||||
|
void onBackendConnectionError(const QString& displayMessage, const QString& rawMessage);
|
||||||
|
void onBackendOutputReceived(const QString& text);
|
||||||
|
void onBackendHostKeyConfirmationRequested(const QString& prompt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Profile m_profile;
|
||||||
|
QThread* m_backendThread;
|
||||||
|
SessionBackend* m_backend;
|
||||||
|
bool m_useKodoTermForSsh;
|
||||||
|
SessionState m_state;
|
||||||
|
QString m_lastError;
|
||||||
|
SessionConnectOptions m_lastConnectOptions;
|
||||||
|
QString m_terminalThemeName;
|
||||||
|
|
||||||
|
KodoTerm* m_sshTerminal;
|
||||||
|
TerminalView* m_terminalOutput;
|
||||||
|
QPlainTextEdit* m_eventLog;
|
||||||
|
QToolButton* m_toggleEventsButton;
|
||||||
|
QWidget* m_eventsPanel;
|
||||||
|
|
||||||
|
void setupUi();
|
||||||
|
std::optional<SessionConnectOptions> buildConnectOptions();
|
||||||
|
bool validateProfileForConnect();
|
||||||
|
void appendEvent(const QString& message);
|
||||||
|
void setState(SessionState state, const QString& message);
|
||||||
|
QString stateSuffix() const;
|
||||||
|
void refreshActionButtons();
|
||||||
|
void setPanelExpanded(QToolButton* button, QWidget* panel, const QString& name, bool expanded);
|
||||||
|
bool startSshTerminal(const SessionConnectOptions& options);
|
||||||
|
void applyTerminalTheme(const QString& themeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,45 +1,142 @@
|
|||||||
#include "session_window.h"
|
#include "session_window.h"
|
||||||
|
|
||||||
#include <QFont>
|
#include "session_tab.h"
|
||||||
#include <QLabel>
|
|
||||||
#include <QTabWidget>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
SessionWindow::SessionWindow(const QString& profileName, QWidget* parent)
|
#include <QAction>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QPalette>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QTabBar>
|
||||||
|
#include <QTabWidget>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QColor tabColorForState(SessionState state, const QPalette& palette)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case SessionState::Disconnected:
|
||||||
|
return palette.color(QPalette::WindowText);
|
||||||
|
case SessionState::Connecting:
|
||||||
|
return QColor(QStringLiteral("#9a6700"));
|
||||||
|
case SessionState::Connected:
|
||||||
|
return QColor(QStringLiteral("#2e7d32"));
|
||||||
|
case SessionState::Failed:
|
||||||
|
return QColor(QStringLiteral("#c62828"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return palette.color(QPalette::WindowText);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList terminalThemeNames()
|
||||||
|
{
|
||||||
|
return {QStringLiteral("Dark"), QStringLiteral("Light"), QStringLiteral("Solarized Dark")};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionWindow::SessionWindow(const Profile& profile, QWidget* parent)
|
||||||
: QMainWindow(parent), m_tabs(new QTabWidget(this))
|
: QMainWindow(parent), m_tabs(new QTabWidget(this))
|
||||||
{
|
{
|
||||||
setWindowTitle(QStringLiteral("OrbitHub Session - %1").arg(profileName));
|
setWindowTitle(QStringLiteral("OrbitHub Session - %1").arg(profile.name));
|
||||||
resize(900, 600);
|
resize(1080, 760);
|
||||||
|
|
||||||
|
m_tabs->setTabsClosable(true);
|
||||||
|
connect(m_tabs,
|
||||||
|
&QTabWidget::tabCloseRequested,
|
||||||
|
this,
|
||||||
|
[this](int index) {
|
||||||
|
QWidget* tab = m_tabs->widget(index);
|
||||||
|
if (auto* sessionTab = qobject_cast<SessionTab*>(tab)) {
|
||||||
|
sessionTab->disconnectSession();
|
||||||
|
}
|
||||||
|
m_tabs->removeTab(index);
|
||||||
|
delete tab;
|
||||||
|
if (m_tabs->count() == 0) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_tabs->tabBar()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(m_tabs->tabBar(),
|
||||||
|
&QWidget::customContextMenuRequested,
|
||||||
|
this,
|
||||||
|
[this](const QPoint& pos) {
|
||||||
|
const int index = m_tabs->tabBar()->tabAt(pos);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* tab = qobject_cast<SessionTab*>(m_tabs->widget(index));
|
||||||
|
if (tab == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu menu(this);
|
||||||
|
QAction* disconnectAction = menu.addAction(QStringLiteral("Disconnect"));
|
||||||
|
QAction* reconnectAction = menu.addAction(QStringLiteral("Reconnect"));
|
||||||
|
menu.addSeparator();
|
||||||
|
QMenu* themeMenu = menu.addMenu(QStringLiteral("Theme"));
|
||||||
|
QList<QAction*> themeActions;
|
||||||
|
const QString currentTheme = tab->terminalThemeName();
|
||||||
|
for (const QString& themeName : terminalThemeNames()) {
|
||||||
|
QAction* themeAction = themeMenu->addAction(themeName);
|
||||||
|
themeAction->setCheckable(true);
|
||||||
|
themeAction->setChecked(
|
||||||
|
themeName.compare(currentTheme, Qt::CaseInsensitive) == 0);
|
||||||
|
themeActions.append(themeAction);
|
||||||
|
}
|
||||||
|
QAction* clearAction = menu.addAction(QStringLiteral("Clear"));
|
||||||
|
QAction* chosen = menu.exec(m_tabs->tabBar()->mapToGlobal(pos));
|
||||||
|
if (chosen == disconnectAction) {
|
||||||
|
tab->disconnectSession();
|
||||||
|
} else if (chosen == reconnectAction) {
|
||||||
|
tab->reconnectSession();
|
||||||
|
} else if (chosen == clearAction) {
|
||||||
|
tab->clearTerminal();
|
||||||
|
} else {
|
||||||
|
for (QAction* themeAction : themeActions) {
|
||||||
|
if (chosen == themeAction) {
|
||||||
|
tab->setTerminalThemeName(themeAction->text());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setCentralWidget(m_tabs);
|
setCentralWidget(m_tabs);
|
||||||
addPlaceholderTab(profileName);
|
addSessionTab(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionWindow::addPlaceholderTab(const QString& profileName)
|
void SessionWindow::addSessionTab(const Profile& profile)
|
||||||
{
|
{
|
||||||
auto* container = new QWidget(this);
|
auto* tab = new SessionTab(profile, this);
|
||||||
auto* layout = new QVBoxLayout(container);
|
const int index = m_tabs->addTab(tab, tab->tabTitle());
|
||||||
|
m_tabs->setCurrentIndex(index);
|
||||||
|
m_tabs->tabBar()->setTabTextColor(
|
||||||
|
index, tabColorForState(SessionState::Disconnected, m_tabs->palette()));
|
||||||
|
|
||||||
auto* titleLabel = new QLabel(QStringLiteral("Profile: %1").arg(profileName), container);
|
connect(tab,
|
||||||
auto* surfaceLabel = new QLabel(QStringLiteral("OrbitHub Native Surface"), container);
|
&SessionTab::tabTitleChanged,
|
||||||
|
this,
|
||||||
QFont titleFont = titleLabel->font();
|
[this, tab](const QString& title) { updateTabTitle(tab, title); });
|
||||||
titleFont.setBold(true);
|
connect(tab,
|
||||||
titleLabel->setFont(titleFont);
|
&SessionTab::tabStateChanged,
|
||||||
|
this,
|
||||||
QFont surfaceFont = surfaceLabel->font();
|
[this, tab](SessionState state) {
|
||||||
surfaceFont.setPointSize(surfaceFont.pointSize() + 6);
|
for (int i = 0; i < m_tabs->count(); ++i) {
|
||||||
surfaceFont.setBold(true);
|
if (m_tabs->widget(i) == tab) {
|
||||||
surfaceLabel->setFont(surfaceFont);
|
m_tabs->tabBar()->setTabTextColor(
|
||||||
|
i, tabColorForState(state, m_tabs->palette()));
|
||||||
surfaceLabel->setAlignment(Qt::AlignCenter);
|
return;
|
||||||
surfaceLabel->setMinimumHeight(200);
|
}
|
||||||
surfaceLabel->setStyleSheet(
|
}
|
||||||
QStringLiteral("border: 1px solid #8a8a8a; background-color: #f5f5f5;"));
|
});
|
||||||
|
}
|
||||||
layout->addWidget(titleLabel);
|
|
||||||
layout->addWidget(surfaceLabel, 1);
|
void SessionWindow::updateTabTitle(SessionTab* tab, const QString& title)
|
||||||
|
{
|
||||||
m_tabs->addTab(container, profileName);
|
for (int i = 0; i < m_tabs->count(); ++i) {
|
||||||
|
if (m_tabs->widget(i) == tab) {
|
||||||
|
m_tabs->setTabText(i, title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
#ifndef ORBITHUB_SESSION_WINDOW_H
|
#ifndef ORBITHUB_SESSION_WINDOW_H
|
||||||
#define ORBITHUB_SESSION_WINDOW_H
|
#define ORBITHUB_SESSION_WINDOW_H
|
||||||
|
|
||||||
|
#include "profile_repository.h"
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class QTabWidget;
|
class QTabWidget;
|
||||||
|
class SessionTab;
|
||||||
|
|
||||||
class SessionWindow : public QMainWindow
|
class SessionWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SessionWindow(const QString& profileName, QWidget* parent = nullptr);
|
explicit SessionWindow(const Profile& profile, QWidget* parent = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTabWidget* m_tabs;
|
QTabWidget* m_tabs;
|
||||||
|
|
||||||
void addPlaceholderTab(const QString& profileName);
|
void addSessionTab(const Profile& profile);
|
||||||
|
void updateTabTitle(SessionTab* tab, const QString& title);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
538
src/ssh_session_backend.cpp
Normal file
538
src/ssh_session_backend.cpp
Normal file
@@ -0,0 +1,538 @@
|
|||||||
|
#include "ssh_session_backend.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QString escapeForShellSingleQuotes(const QString& value)
|
||||||
|
{
|
||||||
|
QString escaped = value;
|
||||||
|
escaped.replace(QStringLiteral("'"), QStringLiteral("'\"'\"'"));
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SshSessionBackend::SshSessionBackend(const Profile& profile, QObject* parent)
|
||||||
|
: SessionBackend(profile, parent),
|
||||||
|
m_process(new QProcess(this)),
|
||||||
|
m_connectedProbeTimer(new QTimer(this)),
|
||||||
|
m_state(SessionState::Disconnected),
|
||||||
|
m_userInitiatedDisconnect(false),
|
||||||
|
m_reconnectPending(false),
|
||||||
|
m_waitingForPasswordPrompt(false),
|
||||||
|
m_waitingForHostKeyConfirmation(false),
|
||||||
|
m_passwordSubmitted(false),
|
||||||
|
m_terminalColumns(0),
|
||||||
|
m_terminalRows(0)
|
||||||
|
{
|
||||||
|
m_connectedProbeTimer->setSingleShot(true);
|
||||||
|
|
||||||
|
connect(m_process, &QProcess::started, this, &SshSessionBackend::onProcessStarted);
|
||||||
|
connect(m_process,
|
||||||
|
&QProcess::errorOccurred,
|
||||||
|
this,
|
||||||
|
&SshSessionBackend::onProcessErrorOccurred);
|
||||||
|
connect(m_process,
|
||||||
|
qOverload<int, QProcess::ExitStatus>(&QProcess::finished),
|
||||||
|
this,
|
||||||
|
&SshSessionBackend::onProcessFinished);
|
||||||
|
connect(m_process,
|
||||||
|
&QProcess::readyReadStandardOutput,
|
||||||
|
this,
|
||||||
|
&SshSessionBackend::onReadyReadStandardOutput);
|
||||||
|
connect(m_process,
|
||||||
|
&QProcess::readyReadStandardError,
|
||||||
|
this,
|
||||||
|
&SshSessionBackend::onReadyReadStandardError);
|
||||||
|
connect(m_connectedProbeTimer,
|
||||||
|
&QTimer::timeout,
|
||||||
|
this,
|
||||||
|
&SshSessionBackend::onConnectedProbeTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
SshSessionBackend::~SshSessionBackend()
|
||||||
|
{
|
||||||
|
if (m_process->state() != QProcess::NotRunning) {
|
||||||
|
m_process->kill();
|
||||||
|
m_process->waitForFinished(500);
|
||||||
|
}
|
||||||
|
cleanupAskPassScript();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::connectSession(const SessionConnectOptions& options)
|
||||||
|
{
|
||||||
|
if (m_state == SessionState::Connected || m_state == SessionState::Connecting) {
|
||||||
|
emit eventLogged(QStringLiteral("Connect skipped: session is already active."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_userInitiatedDisconnect = false;
|
||||||
|
m_reconnectPending = false;
|
||||||
|
m_lastRawError.clear();
|
||||||
|
m_activeOptions = options;
|
||||||
|
m_waitingForPasswordPrompt = false;
|
||||||
|
m_waitingForHostKeyConfirmation = false;
|
||||||
|
m_passwordSubmitted = false;
|
||||||
|
|
||||||
|
if (!startSshProcess(options)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(SessionState::Connecting, QStringLiteral("Connecting to SSH endpoint..."));
|
||||||
|
emit eventLogged(QStringLiteral("Launching ssh client."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::disconnectSession()
|
||||||
|
{
|
||||||
|
if (m_process->state() == QProcess::NotRunning) {
|
||||||
|
if (m_state != SessionState::Disconnected) {
|
||||||
|
setState(SessionState::Disconnected, QStringLiteral("Session is disconnected."));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_userInitiatedDisconnect = true;
|
||||||
|
emit eventLogged(QStringLiteral("Disconnect requested."));
|
||||||
|
m_connectedProbeTimer->stop();
|
||||||
|
|
||||||
|
m_process->terminate();
|
||||||
|
QTimer::singleShot(1500,
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
|
if (m_process->state() != QProcess::NotRunning) {
|
||||||
|
emit eventLogged(QStringLiteral("Force-stopping ssh process."));
|
||||||
|
m_process->kill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::reconnectSession(const SessionConnectOptions& options)
|
||||||
|
{
|
||||||
|
emit eventLogged(QStringLiteral("Reconnect requested."));
|
||||||
|
|
||||||
|
if (m_process->state() == QProcess::NotRunning) {
|
||||||
|
connectSession(options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_reconnectPending = true;
|
||||||
|
m_reconnectOptions = options;
|
||||||
|
m_userInitiatedDisconnect = true;
|
||||||
|
m_process->terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::sendInput(const QString& input)
|
||||||
|
{
|
||||||
|
if (m_process->state() != QProcess::Running) {
|
||||||
|
emit eventLogged(QStringLiteral("Input ignored: session is not running."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_process->write(input.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::confirmHostKey(bool trustHost)
|
||||||
|
{
|
||||||
|
if (m_process->state() != QProcess::Running || !m_waitingForHostKeyConfirmation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_waitingForHostKeyConfirmation = false;
|
||||||
|
const QString response = trustHost ? QStringLiteral("yes\n") : QStringLiteral("no\n");
|
||||||
|
m_process->write(response.toUtf8());
|
||||||
|
|
||||||
|
emit eventLogged(trustHost
|
||||||
|
? QStringLiteral("Host key accepted by user.")
|
||||||
|
: QStringLiteral("Host key rejected by user."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::updateTerminalSize(int columns, int rows)
|
||||||
|
{
|
||||||
|
m_terminalColumns = columns;
|
||||||
|
m_terminalRows = rows;
|
||||||
|
|
||||||
|
if (m_state == SessionState::Connected) {
|
||||||
|
applyTerminalSizeIfAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::onProcessStarted()
|
||||||
|
{
|
||||||
|
emit eventLogged(QStringLiteral("ssh process started."));
|
||||||
|
m_connectedProbeTimer->start(1200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::onProcessErrorOccurred(QProcess::ProcessError)
|
||||||
|
{
|
||||||
|
const QString rawError = m_process->errorString();
|
||||||
|
if (!rawError.isEmpty()) {
|
||||||
|
m_lastRawError += rawError + QLatin1Char('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state == SessionState::Connecting) {
|
||||||
|
const QString display = mapSshError(m_lastRawError);
|
||||||
|
setState(SessionState::Failed, display);
|
||||||
|
emit connectionError(display, m_lastRawError.trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::onProcessFinished(int exitCode, QProcess::ExitStatus)
|
||||||
|
{
|
||||||
|
m_connectedProbeTimer->stop();
|
||||||
|
cleanupAskPassScript();
|
||||||
|
|
||||||
|
if (m_reconnectPending) {
|
||||||
|
m_reconnectPending = false;
|
||||||
|
SessionConnectOptions options = m_reconnectOptions;
|
||||||
|
setState(SessionState::Disconnected, QStringLiteral("Reconnecting..."));
|
||||||
|
QTimer::singleShot(0, this, [this, options]() { connectSession(options); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_userInitiatedDisconnect) {
|
||||||
|
m_userInitiatedDisconnect = false;
|
||||||
|
setState(SessionState::Disconnected, QStringLiteral("Session disconnected."));
|
||||||
|
emit eventLogged(QStringLiteral("ssh process exited after disconnect request."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state == SessionState::Connecting || exitCode != 0) {
|
||||||
|
QString rawError = m_lastRawError.trimmed();
|
||||||
|
if (rawError.isEmpty()) {
|
||||||
|
rawError = QStringLiteral("ssh exited with code %1").arg(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString display = mapSshError(rawError);
|
||||||
|
setState(SessionState::Failed, display);
|
||||||
|
emit connectionError(display, rawError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(SessionState::Disconnected, QStringLiteral("SSH session ended."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::onReadyReadStandardOutput()
|
||||||
|
{
|
||||||
|
const QString chunk = QString::fromUtf8(m_process->readAllStandardOutput());
|
||||||
|
if (chunk.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit outputReceived(chunk);
|
||||||
|
|
||||||
|
if (m_state == SessionState::Connecting && !m_waitingForHostKeyConfirmation
|
||||||
|
&& !m_waitingForPasswordPrompt) {
|
||||||
|
setState(SessionState::Connected, QStringLiteral("SSH session established."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::onReadyReadStandardError()
|
||||||
|
{
|
||||||
|
const QString chunk = QString::fromUtf8(m_process->readAllStandardError());
|
||||||
|
if (chunk.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lastRawError += chunk;
|
||||||
|
emit outputReceived(chunk);
|
||||||
|
|
||||||
|
const QStringList lines = chunk.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
|
||||||
|
for (const QString& line : lines) {
|
||||||
|
const QString trimmed = line.trimmed();
|
||||||
|
if (!trimmed.isEmpty()) {
|
||||||
|
emit eventLogged(trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed.contains(QStringLiteral("Are you sure you want to continue connecting"),
|
||||||
|
Qt::CaseInsensitive)
|
||||||
|
&& !m_waitingForHostKeyConfirmation) {
|
||||||
|
m_waitingForHostKeyConfirmation = true;
|
||||||
|
emit eventLogged(QStringLiteral("Awaiting host key confirmation from user."));
|
||||||
|
emit hostKeyConfirmationRequested(trimmed);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed.contains(QStringLiteral("password:"), Qt::CaseInsensitive)
|
||||||
|
&& profile().authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0
|
||||||
|
&& !m_passwordSubmitted) {
|
||||||
|
if (m_activeOptions.password.isEmpty()) {
|
||||||
|
const QString message = QStringLiteral("Password prompt received but no password is available.");
|
||||||
|
setState(SessionState::Failed, message);
|
||||||
|
emit connectionError(message, trimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_waitingForPasswordPrompt = false;
|
||||||
|
m_passwordSubmitted = true;
|
||||||
|
m_process->write((m_activeOptions.password + QStringLiteral("\n")).toUtf8());
|
||||||
|
emit eventLogged(QStringLiteral("Password prompt received; credentials submitted."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::onConnectedProbeTimeout()
|
||||||
|
{
|
||||||
|
if (m_state != SessionState::Connecting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_process->state() == QProcess::Running && !m_waitingForHostKeyConfirmation
|
||||||
|
&& !m_waitingForPasswordPrompt) {
|
||||||
|
setState(SessionState::Connected, QStringLiteral("SSH session established."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::setState(SessionState state, const QString& message)
|
||||||
|
{
|
||||||
|
m_state = state;
|
||||||
|
emit stateChanged(state, message);
|
||||||
|
emit eventLogged(message);
|
||||||
|
|
||||||
|
if (m_state == SessionState::Connected) {
|
||||||
|
applyTerminalSizeIfAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshSessionBackend::startSshProcess(const SessionConnectOptions& options)
|
||||||
|
{
|
||||||
|
const Profile& p = profile();
|
||||||
|
|
||||||
|
if (p.host.trimmed().isEmpty()) {
|
||||||
|
const QString message = QStringLiteral("Host is required for SSH connections.");
|
||||||
|
setState(SessionState::Failed, message);
|
||||||
|
emit connectionError(message, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.port < 1 || p.port > 65535) {
|
||||||
|
const QString message = QStringLiteral("Port must be between 1 and 65535.");
|
||||||
|
setState(SessionState::Failed, message);
|
||||||
|
emit connectionError(message, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList args;
|
||||||
|
args << QStringLiteral("-tt") << QStringLiteral("-p") << QString::number(p.port)
|
||||||
|
<< QStringLiteral("-o") << QStringLiteral("ConnectTimeout=12") << QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("ServerAliveInterval=20") << QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("ServerAliveCountMax=2");
|
||||||
|
|
||||||
|
const QString policy = options.knownHostsPolicy.trimmed().isEmpty()
|
||||||
|
? p.knownHostsPolicy.trimmed()
|
||||||
|
: options.knownHostsPolicy.trimmed();
|
||||||
|
|
||||||
|
if (policy.compare(QStringLiteral("Ignore"), Qt::CaseInsensitive) == 0) {
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=no")
|
||||||
|
<< QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("UserKnownHostsFile=%1").arg(knownHostsFileForNullDevice());
|
||||||
|
} else if (policy.compare(QStringLiteral("Accept New"), Qt::CaseInsensitive) == 0) {
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=accept-new");
|
||||||
|
} else if (policy.compare(QStringLiteral("Ask"), Qt::CaseInsensitive) == 0) {
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=ask");
|
||||||
|
} else {
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=yes");
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
||||||
|
|
||||||
|
if (p.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0) {
|
||||||
|
if (options.password.isEmpty()) {
|
||||||
|
const QString message = QStringLiteral("Password is required for password authentication.");
|
||||||
|
setState(SessionState::Failed, message);
|
||||||
|
emit connectionError(message, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
args << QStringLiteral("-o") << QStringLiteral("PreferredAuthentications=password")
|
||||||
|
<< QStringLiteral("-o") << QStringLiteral("PubkeyAuthentication=no")
|
||||||
|
<< QStringLiteral("-o") << QStringLiteral("NumberOfPasswordPrompts=1");
|
||||||
|
m_waitingForPasswordPrompt = false;
|
||||||
|
|
||||||
|
QString askPassError;
|
||||||
|
if (!configureAskPass(options, environment, askPassError)) {
|
||||||
|
setState(SessionState::Failed, askPassError);
|
||||||
|
emit connectionError(askPassError, askPassError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (p.authMode.compare(QStringLiteral("Private Key"), Qt::CaseInsensitive) == 0) {
|
||||||
|
QString keyPath = options.privateKeyPath.trimmed();
|
||||||
|
if (keyPath.isEmpty()) {
|
||||||
|
keyPath = p.privateKeyPath.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyPath.isEmpty()) {
|
||||||
|
const QString message = QStringLiteral("Private key path is required.");
|
||||||
|
setState(SessionState::Failed, message);
|
||||||
|
emit connectionError(message, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QFileInfo::exists(keyPath)) {
|
||||||
|
const QString message = QStringLiteral("Private key file does not exist: %1")
|
||||||
|
.arg(keyPath);
|
||||||
|
setState(SessionState::Failed, message);
|
||||||
|
emit connectionError(message, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
args << QStringLiteral("-i") << keyPath << QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("PreferredAuthentications=publickey") << QStringLiteral("-o")
|
||||||
|
<< QStringLiteral("PasswordAuthentication=no");
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString target = p.username.trimmed().isEmpty()
|
||||||
|
? p.host.trimmed()
|
||||||
|
: QStringLiteral("%1@%2").arg(p.username.trimmed(), p.host.trimmed());
|
||||||
|
args << target;
|
||||||
|
|
||||||
|
m_process->setProcessEnvironment(environment);
|
||||||
|
m_process->setProgram(QStringLiteral("ssh"));
|
||||||
|
m_process->setArguments(args);
|
||||||
|
m_process->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
|
|
||||||
|
m_process->start();
|
||||||
|
if (!m_process->waitForStarted(3000)) {
|
||||||
|
const QString rawError = m_process->errorString();
|
||||||
|
const QString display = mapSshError(rawError);
|
||||||
|
setState(SessionState::Failed, display);
|
||||||
|
emit connectionError(display, rawError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SshSessionBackend::configureAskPass(const SessionConnectOptions& options,
|
||||||
|
QProcessEnvironment& environment,
|
||||||
|
QString& error)
|
||||||
|
{
|
||||||
|
cleanupAskPassScript();
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
m_askPassScriptPath = QDir::temp().filePath(
|
||||||
|
QStringLiteral("orbithub_askpass_%1.cmd")
|
||||||
|
.arg(QUuid::createUuid().toString(QUuid::WithoutBraces)));
|
||||||
|
#else
|
||||||
|
m_askPassScriptPath = QDir::temp().filePath(
|
||||||
|
QStringLiteral("orbithub_askpass_%1.sh")
|
||||||
|
.arg(QUuid::createUuid().toString(QUuid::WithoutBraces)));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QFile script(m_askPassScriptPath);
|
||||||
|
if (!script.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||||
|
error = QStringLiteral("Failed to create temporary askpass helper script.");
|
||||||
|
cleanupAskPassScript();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream out(&script);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
out << "@echo off\r\n";
|
||||||
|
out << "echo " << options.password << "\r\n";
|
||||||
|
#else
|
||||||
|
const QString escapedPassword = escapeForShellSingleQuotes(options.password);
|
||||||
|
out << "#!/bin/sh\n";
|
||||||
|
out << "printf '%s\\n' '" << escapedPassword << "'\n";
|
||||||
|
#endif
|
||||||
|
out.flush();
|
||||||
|
script.close();
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
if (!QFile::setPermissions(m_askPassScriptPath,
|
||||||
|
QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)) {
|
||||||
|
error = QStringLiteral("Failed to set permissions on askpass helper script.");
|
||||||
|
cleanupAskPassScript();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
environment.insert(QStringLiteral("SSH_ASKPASS"), m_askPassScriptPath);
|
||||||
|
environment.insert(QStringLiteral("SSH_ASKPASS_REQUIRE"), QStringLiteral("force"));
|
||||||
|
if (!environment.contains(QStringLiteral("DISPLAY"))) {
|
||||||
|
environment.insert(QStringLiteral("DISPLAY"), QStringLiteral(":0"));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::cleanupAskPassScript()
|
||||||
|
{
|
||||||
|
if (!m_askPassScriptPath.isEmpty()) {
|
||||||
|
QFile::remove(m_askPassScriptPath);
|
||||||
|
m_askPassScriptPath.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshSessionBackend::mapSshError(const QString& rawError) const
|
||||||
|
{
|
||||||
|
const QString raw = rawError.trimmed();
|
||||||
|
if (raw.contains(QStringLiteral("Permission denied"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("Authentication failed. Check username and credentials.");
|
||||||
|
}
|
||||||
|
if (raw.contains(QStringLiteral("Host key verification failed"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("Host key verification failed.");
|
||||||
|
}
|
||||||
|
if (raw.contains(QStringLiteral("Could not resolve hostname"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("Host could not be resolved.");
|
||||||
|
}
|
||||||
|
if (raw.contains(QStringLiteral("Connection timed out"), Qt::CaseInsensitive)
|
||||||
|
|| raw.contains(QStringLiteral("Operation timed out"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("Connection timed out.");
|
||||||
|
}
|
||||||
|
if (raw.contains(QStringLiteral("Connection refused"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("Connection refused by remote host.");
|
||||||
|
}
|
||||||
|
if (raw.contains(QStringLiteral("No route to host"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("No route to host.");
|
||||||
|
}
|
||||||
|
if (raw.contains(QStringLiteral("Identity file"), Qt::CaseInsensitive)
|
||||||
|
&& raw.contains(QStringLiteral("not accessible"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("Private key file is not accessible.");
|
||||||
|
}
|
||||||
|
if (raw.contains(QStringLiteral("No such file or directory"), Qt::CaseInsensitive)) {
|
||||||
|
if (raw.contains(QStringLiteral("ssh-askpass"), Qt::CaseInsensitive)) {
|
||||||
|
return QStringLiteral("SSH password helper is missing or failed to launch.");
|
||||||
|
}
|
||||||
|
return QStringLiteral("Required file was not found.");
|
||||||
|
}
|
||||||
|
if (raw.isEmpty()) {
|
||||||
|
return QStringLiteral("SSH connection failed for an unknown reason.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QStringLiteral("SSH connection failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SshSessionBackend::knownHostsFileForNullDevice() const
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return QStringLiteral("NUL");
|
||||||
|
#else
|
||||||
|
return QStringLiteral("/dev/null");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshSessionBackend::applyTerminalSizeIfAvailable()
|
||||||
|
{
|
||||||
|
if (m_process->state() != QProcess::Running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_terminalColumns <= 0 || m_terminalRows <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString command = QStringLiteral("stty cols %1 rows %2\\n")
|
||||||
|
.arg(m_terminalColumns)
|
||||||
|
.arg(m_terminalRows);
|
||||||
|
m_process->write(command.toUtf8());
|
||||||
|
emit eventLogged(
|
||||||
|
QStringLiteral("Applied terminal size: %1x%2").arg(m_terminalColumns).arg(m_terminalRows));
|
||||||
|
}
|
||||||
61
src/ssh_session_backend.h
Normal file
61
src/ssh_session_backend.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#ifndef ORBITHUB_SSH_SESSION_BACKEND_H
|
||||||
|
#define ORBITHUB_SSH_SESSION_BACKEND_H
|
||||||
|
|
||||||
|
#include "session_backend.h"
|
||||||
|
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QString>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class SshSessionBackend : public SessionBackend
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SshSessionBackend(const Profile& profile, QObject* parent = nullptr);
|
||||||
|
~SshSessionBackend() override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void connectSession(const SessionConnectOptions& options) override;
|
||||||
|
void disconnectSession() override;
|
||||||
|
void reconnectSession(const SessionConnectOptions& options) override;
|
||||||
|
void sendInput(const QString& input) override;
|
||||||
|
void confirmHostKey(bool trustHost) override;
|
||||||
|
void updateTerminalSize(int columns, int rows) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onProcessStarted();
|
||||||
|
void onProcessErrorOccurred(QProcess::ProcessError error);
|
||||||
|
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
void onReadyReadStandardOutput();
|
||||||
|
void onReadyReadStandardError();
|
||||||
|
void onConnectedProbeTimeout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QProcess* m_process;
|
||||||
|
QTimer* m_connectedProbeTimer;
|
||||||
|
SessionState m_state;
|
||||||
|
bool m_userInitiatedDisconnect;
|
||||||
|
bool m_reconnectPending;
|
||||||
|
SessionConnectOptions m_reconnectOptions;
|
||||||
|
SessionConnectOptions m_activeOptions;
|
||||||
|
QString m_lastRawError;
|
||||||
|
QString m_askPassScriptPath;
|
||||||
|
bool m_waitingForPasswordPrompt;
|
||||||
|
bool m_waitingForHostKeyConfirmation;
|
||||||
|
bool m_passwordSubmitted;
|
||||||
|
int m_terminalColumns;
|
||||||
|
int m_terminalRows;
|
||||||
|
|
||||||
|
void setState(SessionState state, const QString& message);
|
||||||
|
bool startSshProcess(const SessionConnectOptions& options);
|
||||||
|
bool configureAskPass(const SessionConnectOptions& options,
|
||||||
|
QProcessEnvironment& environment,
|
||||||
|
QString& error);
|
||||||
|
void cleanupAskPassScript();
|
||||||
|
QString mapSshError(const QString& rawError) const;
|
||||||
|
QString knownHostsFileForNullDevice() const;
|
||||||
|
void applyTerminalSizeIfAvailable();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
520
src/terminal_view.cpp
Normal file
520
src/terminal_view.cpp
Normal file
@@ -0,0 +1,520 @@
|
|||||||
|
#include "terminal_view.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QFocusEvent>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QTextCursor>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QString normalizedThemeName(const QString& value)
|
||||||
|
{
|
||||||
|
return value.trimmed().toLower();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalView::TerminalView(QWidget* parent)
|
||||||
|
: QTextEdit(parent),
|
||||||
|
m_bold(false),
|
||||||
|
m_hasFgColor(false),
|
||||||
|
m_hasBgColor(false)
|
||||||
|
{
|
||||||
|
setReadOnly(false);
|
||||||
|
setUndoRedoEnabled(false);
|
||||||
|
setAcceptRichText(false);
|
||||||
|
setLineWrapMode(QTextEdit::NoWrap);
|
||||||
|
setContextMenuPolicy(Qt::NoContextMenu);
|
||||||
|
setCursorWidth(2);
|
||||||
|
document()->setMaximumBlockCount(4000);
|
||||||
|
|
||||||
|
applyThemePalette(paletteByName(QStringLiteral("Dark")));
|
||||||
|
resetSgrState();
|
||||||
|
|
||||||
|
QTimer::singleShot(0, this, [this]() {
|
||||||
|
moveCursor(QTextCursor::End);
|
||||||
|
emitTerminalSize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TerminalView::themeNames()
|
||||||
|
{
|
||||||
|
return {QStringLiteral("Dark"), QStringLiteral("Light"), QStringLiteral("Solarized Dark")};
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::setThemeName(const QString& themeName)
|
||||||
|
{
|
||||||
|
applyThemePalette(paletteByName(themeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::appendTerminalData(const QString& data)
|
||||||
|
{
|
||||||
|
if (data.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString merged = m_pendingEscape + data;
|
||||||
|
m_pendingEscape.clear();
|
||||||
|
|
||||||
|
QString plainBuffer;
|
||||||
|
|
||||||
|
for (int i = 0; i < merged.size();) {
|
||||||
|
const QChar ch = merged.at(i);
|
||||||
|
|
||||||
|
if (ch == QChar::fromLatin1('\x1b')) {
|
||||||
|
if (!plainBuffer.isEmpty()) {
|
||||||
|
appendTextChunk(plainBuffer);
|
||||||
|
plainBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 >= merged.size()) {
|
||||||
|
m_pendingEscape = merged.mid(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (merged.at(i + 1) != QChar::fromLatin1('[')) {
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int end = i + 2;
|
||||||
|
while (end < merged.size()) {
|
||||||
|
const ushort c = merged.at(end).unicode();
|
||||||
|
if (c >= 0x40 && c <= 0x7e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end >= merged.size()) {
|
||||||
|
m_pendingEscape = merged.mid(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QChar finalByte = merged.at(end);
|
||||||
|
const QString params = merged.mid(i + 2, end - (i + 2));
|
||||||
|
|
||||||
|
if (finalByte == QChar::fromLatin1('m')) {
|
||||||
|
handleSgrSequence(params);
|
||||||
|
} else if (finalByte == QChar::fromLatin1('J')) {
|
||||||
|
if (params.isEmpty() || params == QStringLiteral("2")) {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = end + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == QChar::fromLatin1('\r')) {
|
||||||
|
const bool hasLfAfter = (i + 1 < merged.size() && merged.at(i + 1) == QChar::fromLatin1('\n'));
|
||||||
|
if (!hasLfAfter) {
|
||||||
|
plainBuffer.append(QChar::fromLatin1('\n'));
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
plainBuffer.append(ch);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!plainBuffer.isEmpty()) {
|
||||||
|
appendTextChunk(plainBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
if (event == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
moveCursor(QTextCursor::End);
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers = event->modifiers();
|
||||||
|
|
||||||
|
if (modifiers == (Qt::ControlModifier | Qt::ShiftModifier)
|
||||||
|
&& event->key() == Qt::Key_C) {
|
||||||
|
const QString selected = textCursor().selectedText();
|
||||||
|
if (!selected.isEmpty()) {
|
||||||
|
QApplication::clipboard()->setText(selected);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifiers == Qt::ControlModifier) {
|
||||||
|
switch (event->key()) {
|
||||||
|
case Qt::Key_C:
|
||||||
|
emit inputGenerated(QStringLiteral("\x03"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_D:
|
||||||
|
emit inputGenerated(QStringLiteral("\x04"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_L:
|
||||||
|
emit inputGenerated(QStringLiteral("\x0c"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_V: {
|
||||||
|
const QString clipboardText = QApplication::clipboard()->text();
|
||||||
|
if (!clipboardText.isEmpty()) {
|
||||||
|
emit inputGenerated(clipboardText);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event->key()) {
|
||||||
|
case Qt::Key_Return:
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
emit inputGenerated(QStringLiteral("\n"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
emit inputGenerated(QStringLiteral("\x7f"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
emit inputGenerated(QStringLiteral("\t"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Left:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[D"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Right:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[C"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Up:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[A"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Down:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[B"));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString text = event->text();
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
emit inputGenerated(text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::focusInEvent(QFocusEvent* event)
|
||||||
|
{
|
||||||
|
QTextEdit::focusInEvent(event);
|
||||||
|
moveCursor(QTextCursor::End);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::resizeEvent(QResizeEvent* event)
|
||||||
|
{
|
||||||
|
QTextEdit::resizeEvent(event);
|
||||||
|
emitTerminalSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalView::ThemePalette TerminalView::paletteByName(const QString& themeName)
|
||||||
|
{
|
||||||
|
const QString theme = normalizedThemeName(themeName);
|
||||||
|
|
||||||
|
if (theme == QStringLiteral("light")) {
|
||||||
|
return ThemePalette{QStringLiteral("Light"),
|
||||||
|
QColor(QStringLiteral("#ececec")),
|
||||||
|
QColor(QStringLiteral("#000000")),
|
||||||
|
{QColor(QStringLiteral("#000000")),
|
||||||
|
QColor(QStringLiteral("#aa0000")),
|
||||||
|
QColor(QStringLiteral("#008000")),
|
||||||
|
QColor(QStringLiteral("#7a5f00")),
|
||||||
|
QColor(QStringLiteral("#0033cc")),
|
||||||
|
QColor(QStringLiteral("#8a00a8")),
|
||||||
|
QColor(QStringLiteral("#005f87")),
|
||||||
|
QColor(QStringLiteral("#333333"))},
|
||||||
|
{QColor(QStringLiteral("#5c5c5c")),
|
||||||
|
QColor(QStringLiteral("#d30000")),
|
||||||
|
QColor(QStringLiteral("#00a000")),
|
||||||
|
QColor(QStringLiteral("#9a7700")),
|
||||||
|
QColor(QStringLiteral("#0055ff")),
|
||||||
|
QColor(QStringLiteral("#b300db")),
|
||||||
|
QColor(QStringLiteral("#007ea7")),
|
||||||
|
QColor(QStringLiteral("#111111"))}};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme == QStringLiteral("solarized dark")) {
|
||||||
|
return ThemePalette{QStringLiteral("Solarized Dark"),
|
||||||
|
QColor(QStringLiteral("#002b36")),
|
||||||
|
QColor(QStringLiteral("#839496")),
|
||||||
|
{QColor(QStringLiteral("#073642")),
|
||||||
|
QColor(QStringLiteral("#dc322f")),
|
||||||
|
QColor(QStringLiteral("#859900")),
|
||||||
|
QColor(QStringLiteral("#b58900")),
|
||||||
|
QColor(QStringLiteral("#268bd2")),
|
||||||
|
QColor(QStringLiteral("#d33682")),
|
||||||
|
QColor(QStringLiteral("#2aa198")),
|
||||||
|
QColor(QStringLiteral("#eee8d5"))},
|
||||||
|
{QColor(QStringLiteral("#586e75")),
|
||||||
|
QColor(QStringLiteral("#cb4b16")),
|
||||||
|
QColor(QStringLiteral("#586e75")),
|
||||||
|
QColor(QStringLiteral("#657b83")),
|
||||||
|
QColor(QStringLiteral("#839496")),
|
||||||
|
QColor(QStringLiteral("#6c71c4")),
|
||||||
|
QColor(QStringLiteral("#93a1a1")),
|
||||||
|
QColor(QStringLiteral("#fdf6e3"))}};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ThemePalette{QStringLiteral("Dark"),
|
||||||
|
QColor(QStringLiteral("#1e1e1e")),
|
||||||
|
QColor(QStringLiteral("#d4d4d4")),
|
||||||
|
{QColor(QStringLiteral("#000000")),
|
||||||
|
QColor(QStringLiteral("#cd3131")),
|
||||||
|
QColor(QStringLiteral("#0dbc79")),
|
||||||
|
QColor(QStringLiteral("#e5e510")),
|
||||||
|
QColor(QStringLiteral("#2472c8")),
|
||||||
|
QColor(QStringLiteral("#bc3fbc")),
|
||||||
|
QColor(QStringLiteral("#11a8cd")),
|
||||||
|
QColor(QStringLiteral("#e5e5e5"))},
|
||||||
|
{QColor(QStringLiteral("#666666")),
|
||||||
|
QColor(QStringLiteral("#f14c4c")),
|
||||||
|
QColor(QStringLiteral("#23d18b")),
|
||||||
|
QColor(QStringLiteral("#f5f543")),
|
||||||
|
QColor(QStringLiteral("#3b8eea")),
|
||||||
|
QColor(QStringLiteral("#d670d6")),
|
||||||
|
QColor(QStringLiteral("#29b8db")),
|
||||||
|
QColor(QStringLiteral("#ffffff"))}};
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TerminalView::colorFrom256Index(int index)
|
||||||
|
{
|
||||||
|
if (index < 0) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
if (index > 255) {
|
||||||
|
index = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 16) {
|
||||||
|
static const std::array<QColor, 16> base = {
|
||||||
|
QColor(QStringLiteral("#000000")), QColor(QStringLiteral("#800000")),
|
||||||
|
QColor(QStringLiteral("#008000")), QColor(QStringLiteral("#808000")),
|
||||||
|
QColor(QStringLiteral("#000080")), QColor(QStringLiteral("#800080")),
|
||||||
|
QColor(QStringLiteral("#008080")), QColor(QStringLiteral("#c0c0c0")),
|
||||||
|
QColor(QStringLiteral("#808080")), QColor(QStringLiteral("#ff0000")),
|
||||||
|
QColor(QStringLiteral("#00ff00")), QColor(QStringLiteral("#ffff00")),
|
||||||
|
QColor(QStringLiteral("#0000ff")), QColor(QStringLiteral("#ff00ff")),
|
||||||
|
QColor(QStringLiteral("#00ffff")), QColor(QStringLiteral("#ffffff"))};
|
||||||
|
return base.at(static_cast<size_t>(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 16 && index <= 231) {
|
||||||
|
const int c = index - 16;
|
||||||
|
const int r = c / 36;
|
||||||
|
const int g = (c / 6) % 6;
|
||||||
|
const int b = c % 6;
|
||||||
|
|
||||||
|
const auto channel = [](int v) { return v == 0 ? 0 : 55 + v * 40; };
|
||||||
|
return QColor(channel(r), channel(g), channel(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
const int gray = 8 + (index - 232) * 10;
|
||||||
|
return QColor(gray, gray, gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::applyThemePalette(const ThemePalette& palette)
|
||||||
|
{
|
||||||
|
m_palette = palette;
|
||||||
|
|
||||||
|
const QString stylesheet = QStringLiteral("QTextEdit { background: %1; color: %2; }")
|
||||||
|
.arg(m_palette.background.name(), m_palette.foreground.name());
|
||||||
|
setStyleSheet(stylesheet);
|
||||||
|
|
||||||
|
if (!m_hasFgColor) {
|
||||||
|
m_fgColor = m_palette.foreground;
|
||||||
|
}
|
||||||
|
if (!m_hasBgColor) {
|
||||||
|
m_bgColor = m_palette.background;
|
||||||
|
}
|
||||||
|
applyCurrentFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::applyCurrentFormat()
|
||||||
|
{
|
||||||
|
m_currentFormat = QTextCharFormat();
|
||||||
|
m_currentFormat.setForeground(m_hasFgColor ? m_fgColor : m_palette.foreground);
|
||||||
|
if (m_hasBgColor) {
|
||||||
|
m_currentFormat.setBackground(m_bgColor);
|
||||||
|
}
|
||||||
|
QFont font = currentFont();
|
||||||
|
font.setBold(m_bold);
|
||||||
|
m_currentFormat.setFont(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::resetSgrState()
|
||||||
|
{
|
||||||
|
m_bold = false;
|
||||||
|
m_hasFgColor = false;
|
||||||
|
m_hasBgColor = false;
|
||||||
|
m_fgColor = m_palette.foreground;
|
||||||
|
m_bgColor = m_palette.background;
|
||||||
|
applyCurrentFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::handleSgrSequence(const QString& params)
|
||||||
|
{
|
||||||
|
QStringList parts = params.split(QChar::fromLatin1(';'), Qt::KeepEmptyParts);
|
||||||
|
if (parts.isEmpty()) {
|
||||||
|
parts.push_back(QStringLiteral("0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.size(); ++i) {
|
||||||
|
const QString part = parts.at(i).trimmed();
|
||||||
|
bool ok = false;
|
||||||
|
const int code = part.isEmpty() ? 0 : part.toInt(&ok);
|
||||||
|
if (!ok && !part.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == 0) {
|
||||||
|
resetSgrState();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code == 1) {
|
||||||
|
m_bold = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code == 22) {
|
||||||
|
m_bold = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code == 39) {
|
||||||
|
m_hasFgColor = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code == 49) {
|
||||||
|
m_hasBgColor = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code >= 30 && code <= 37) {
|
||||||
|
m_fgColor = paletteColor(false, code - 30, false);
|
||||||
|
m_hasFgColor = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code >= 90 && code <= 97) {
|
||||||
|
m_fgColor = paletteColor(false, code - 90, true);
|
||||||
|
m_hasFgColor = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code >= 40 && code <= 47) {
|
||||||
|
m_bgColor = paletteColor(true, code - 40, false);
|
||||||
|
m_hasBgColor = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code >= 100 && code <= 107) {
|
||||||
|
m_bgColor = paletteColor(true, code - 100, true);
|
||||||
|
m_hasBgColor = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == 38 || code == 48) {
|
||||||
|
const bool background = (code == 48);
|
||||||
|
if (i + 1 >= parts.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int mode = parts.at(i + 1).toInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 5 && i + 2 < parts.size()) {
|
||||||
|
const int index = parts.at(i + 2).toInt(&ok);
|
||||||
|
if (ok) {
|
||||||
|
const QColor color = colorFrom256Index(index);
|
||||||
|
if (background) {
|
||||||
|
m_bgColor = color;
|
||||||
|
m_hasBgColor = true;
|
||||||
|
} else {
|
||||||
|
m_fgColor = color;
|
||||||
|
m_hasFgColor = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 2 && i + 4 < parts.size()) {
|
||||||
|
const int r = parts.at(i + 2).toInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const int g = parts.at(i + 3).toInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const int b = parts.at(i + 4).toInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QColor color(r, g, b);
|
||||||
|
if (background) {
|
||||||
|
m_bgColor = color;
|
||||||
|
m_hasBgColor = true;
|
||||||
|
} else {
|
||||||
|
m_fgColor = color;
|
||||||
|
m_hasFgColor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyCurrentFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::appendTextChunk(const QString& text)
|
||||||
|
{
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
cursor.insertText(text, m_currentFormat);
|
||||||
|
setTextCursor(cursor);
|
||||||
|
ensureCursorVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TerminalView::paletteColor(bool, int index, bool bright) const
|
||||||
|
{
|
||||||
|
const int safeIndex = std::clamp(index, 0, 7);
|
||||||
|
return bright ? m_palette.bright.at(static_cast<size_t>(safeIndex))
|
||||||
|
: m_palette.normal.at(static_cast<size_t>(safeIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
int TerminalView::terminalColumns() const
|
||||||
|
{
|
||||||
|
const QFontMetrics metrics(font());
|
||||||
|
const int cellWidth = std::max(1, metrics.horizontalAdvance(QChar::fromLatin1('M')));
|
||||||
|
return std::max(1, viewport()->width() / cellWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TerminalView::terminalRows() const
|
||||||
|
{
|
||||||
|
const QFontMetrics metrics(font());
|
||||||
|
const int cellHeight = std::max(1, metrics.lineSpacing());
|
||||||
|
return std::max(1, viewport()->height() / cellHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::emitTerminalSize()
|
||||||
|
{
|
||||||
|
emit terminalSizeChanged(terminalColumns(), terminalRows());
|
||||||
|
}
|
||||||
66
src/terminal_view.h
Normal file
66
src/terminal_view.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef ORBITHUB_TERMINAL_VIEW_H
|
||||||
|
#define ORBITHUB_TERMINAL_VIEW_H
|
||||||
|
|
||||||
|
#include <QTextEdit>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
class QKeyEvent;
|
||||||
|
class QFocusEvent;
|
||||||
|
class QResizeEvent;
|
||||||
|
|
||||||
|
class TerminalView : public QTextEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TerminalView(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
static QStringList themeNames();
|
||||||
|
void setThemeName(const QString& themeName);
|
||||||
|
void appendTerminalData(const QString& data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void inputGenerated(const QString& input);
|
||||||
|
void terminalSizeChanged(int columns, int rows);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent* event) override;
|
||||||
|
void focusInEvent(QFocusEvent* event) override;
|
||||||
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ThemePalette {
|
||||||
|
QString name;
|
||||||
|
QColor background;
|
||||||
|
QColor foreground;
|
||||||
|
std::array<QColor, 8> normal;
|
||||||
|
std::array<QColor, 8> bright;
|
||||||
|
};
|
||||||
|
|
||||||
|
ThemePalette m_palette;
|
||||||
|
QString m_pendingEscape;
|
||||||
|
QString m_rawHistory;
|
||||||
|
bool m_bold;
|
||||||
|
bool m_hasFgColor;
|
||||||
|
bool m_hasBgColor;
|
||||||
|
QColor m_fgColor;
|
||||||
|
QColor m_bgColor;
|
||||||
|
QTextCharFormat m_currentFormat;
|
||||||
|
|
||||||
|
static ThemePalette paletteByName(const QString& themeName);
|
||||||
|
static QColor colorFrom256Index(int index);
|
||||||
|
|
||||||
|
void applyThemePalette(const ThemePalette& palette);
|
||||||
|
void applyCurrentFormat();
|
||||||
|
void resetSgrState();
|
||||||
|
void handleSgrSequence(const QString& params);
|
||||||
|
void appendTextChunk(const QString& text);
|
||||||
|
QColor paletteColor(bool background, int index, bool bright) const;
|
||||||
|
void processData(const QString& data, bool storeInHistory);
|
||||||
|
int terminalColumns() const;
|
||||||
|
int terminalRows() const;
|
||||||
|
void emitTerminalSize();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
39
src/unsupported_session_backend.cpp
Normal file
39
src/unsupported_session_backend.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#include "unsupported_session_backend.h"
|
||||||
|
|
||||||
|
UnsupportedSessionBackend::UnsupportedSessionBackend(const Profile& profile, QObject* parent)
|
||||||
|
: SessionBackend(profile, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnsupportedSessionBackend::connectSession(const SessionConnectOptions&)
|
||||||
|
{
|
||||||
|
const QString message = QStringLiteral("Protocol '%1' is not implemented yet.")
|
||||||
|
.arg(profile().protocol);
|
||||||
|
emit eventLogged(message);
|
||||||
|
emit stateChanged(SessionState::Failed, message);
|
||||||
|
emit connectionError(message, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnsupportedSessionBackend::disconnectSession()
|
||||||
|
{
|
||||||
|
emit stateChanged(SessionState::Disconnected,
|
||||||
|
QStringLiteral("No active connection for this protocol."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnsupportedSessionBackend::reconnectSession(const SessionConnectOptions& options)
|
||||||
|
{
|
||||||
|
connectSession(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnsupportedSessionBackend::sendInput(const QString&)
|
||||||
|
{
|
||||||
|
emit eventLogged(QStringLiteral("Input ignored: protocol backend is not interactive."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnsupportedSessionBackend::confirmHostKey(bool)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnsupportedSessionBackend::updateTerminalSize(int, int)
|
||||||
|
{
|
||||||
|
}
|
||||||
22
src/unsupported_session_backend.h
Normal file
22
src/unsupported_session_backend.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef ORBITHUB_UNSUPPORTED_SESSION_BACKEND_H
|
||||||
|
#define ORBITHUB_UNSUPPORTED_SESSION_BACKEND_H
|
||||||
|
|
||||||
|
#include "session_backend.h"
|
||||||
|
|
||||||
|
class UnsupportedSessionBackend : public SessionBackend
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit UnsupportedSessionBackend(const Profile& profile, QObject* parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void connectSession(const SessionConnectOptions& options) override;
|
||||||
|
void disconnectSession() override;
|
||||||
|
void reconnectSession(const SessionConnectOptions& options) override;
|
||||||
|
void sendInput(const QString& input) override;
|
||||||
|
void confirmHostKey(bool trustHost) override;
|
||||||
|
void updateTerminalSize(int columns, int rows) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
61
third_party/KodoTerm/CMakeLists.txt
vendored
Normal file
61
third_party/KodoTerm/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(KodoTermVendor LANGUAGES C CXX)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
|
set(LIBVTERM_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../libvterm")
|
||||||
|
|
||||||
|
file(GLOB VTERM_SOURCES CONFIGURE_DEPENDS
|
||||||
|
"${LIBVTERM_SOURCE_DIR}/src/*.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(vterm STATIC ${VTERM_SOURCES})
|
||||||
|
target_include_directories(vterm PUBLIC
|
||||||
|
"${LIBVTERM_SOURCE_DIR}/include"
|
||||||
|
"${LIBVTERM_SOURCE_DIR}/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(KODOTERM_SOURCES
|
||||||
|
src/KodoTerm.cpp
|
||||||
|
src/KodoTermConfig.cpp
|
||||||
|
src/PtyProcess.cpp
|
||||||
|
src/PtyProcess.h
|
||||||
|
include/KodoTerm/KodoTerm.hpp
|
||||||
|
include/KodoTerm/KodoTermConfig.hpp
|
||||||
|
KodoTermThemes.qrc
|
||||||
|
)
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
list(APPEND KODOTERM_SOURCES
|
||||||
|
src/PtyProcess_unix.cpp
|
||||||
|
src/PtyProcess_unix.h
|
||||||
|
)
|
||||||
|
elseif(WIN32)
|
||||||
|
list(APPEND KODOTERM_SOURCES
|
||||||
|
src/PtyProcess_win.cpp
|
||||||
|
src/PtyProcess_win.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(KodoTerm ${KODOTERM_SOURCES})
|
||||||
|
add_library(KodoTerm::KodoTerm ALIAS KodoTerm)
|
||||||
|
|
||||||
|
target_compile_features(KodoTerm PUBLIC cxx_std_20)
|
||||||
|
|
||||||
|
target_include_directories(KodoTerm PRIVATE src)
|
||||||
|
target_include_directories(KodoTerm PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(KodoTerm PUBLIC
|
||||||
|
Qt6::Core
|
||||||
|
Qt6::Gui
|
||||||
|
Qt6::Widgets
|
||||||
|
vterm
|
||||||
|
)
|
||||||
470
third_party/KodoTerm/KodoTermThemes.qrc
vendored
Normal file
470
third_party/KodoTerm/KodoTermThemes.qrc
vendored
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>KodoTermThemes/konsole/BlackOnLightYellow.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/BlackOnRandomLight.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/BlackOnWhite.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/BlueOnBlack.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/Breeze.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/Campbell.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/DarkPastels.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/GreenOnBlack.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/Linux.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/RedOnBlack.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/Solarized.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/SolarizedLight.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/konsole/WhiteOnBlack.colorscheme</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/0x96f.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/12-bit Rainbow.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/3024 Day.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/3024 Night.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Aardvark Blue.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Abernathy.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Adventure Time.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Adventure.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Adwaita Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Adwaita.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Afterglow.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Aizen Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Aizen Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Alabaster.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Alien Blood.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Andromeda.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Apple Classic.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Apple System Colors Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Apple System Colors.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Arcoiris.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ardoise.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Argonaut.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Arthur.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Atelier Sulphurpool.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Atom One Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Atom One Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Atom.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Aura.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Aurora.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ayu Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ayu Mirage.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ayu.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Banana Blueberry.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Batman.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Belafonte Day.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Belafonte Night.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Birds Of Paradise.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Bathory).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Burzum).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Dark Funeral).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Gorgoroth).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Immortal).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Khold).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Marduk).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Mayhem).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Nile).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal (Venom).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Black Metal.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Blazer.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Blue Berry Pie.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Blue Dolphin.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Blue Matrix.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Bluloco Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Bluloco Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Borland.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Box.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Breadog.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Breeze.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Bright Lights.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Broadcast.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Brogrammer.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Builtin Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Builtin Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Builtin Pastel Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Builtin Tango Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Builtin Tango Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/C64.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/CGA.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/CLRS.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Calamity.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Carbonfox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Catppuccin Frappe.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Catppuccin Latte.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Catppuccin Macchiato.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Catppuccin Mocha.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Chalk.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Chalkboard.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Challenger Deep.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Chester.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ciapre.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Citruszest.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cobalt Neon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cobalt Next Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cobalt Next Minimal.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cobalt Next.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cobalt2.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Coffee Theme.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Crayon Pony Fish.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cursor Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cutie Pro.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cyberdyne.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cyberpunk Scarlet Protocol.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Cyberpunk.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dark Modern.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dark Pastel.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dark+.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Darkermatrix.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Darkmatrix.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Darkside.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dawnfox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dayfox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Deep.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Desert.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Detuned.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dimidium.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dimmed Monokai.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Django Reborn Again.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Django Smooth.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Django.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Doom One.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Doom Peacock.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dot Gov.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dracula+.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Dracula.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Duckbones.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Duotone Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Duskfox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/ENCOM.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Earthsong.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Electron Highlighter.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Elegant.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Elemental.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Elementary.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Embark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Embers Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Espresso Libre.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Espresso.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Everblush.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Everforest Dark Hard.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Everforest Light Med.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Fahrenheit.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Fairyfloss.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Farmhouse Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Farmhouse Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Fideloper.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Firefly Traditional.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Firefox Dev.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Firewatch.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Fish Tank.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Flat.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Flatland.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Flexoki Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Flexoki Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Floraverse.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Forest Blue.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Framer.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Front End Delight.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Fun Forrest.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Galaxy.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Galizur.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ghostty Default Style Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Dark Colorblind.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Dark Default.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Dark Dimmed.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Dark High Contrast.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Light Colorblind.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Light Default.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub Light High Contrast.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitHub.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitLab Dark Grey.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitLab Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/GitLab Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Glacier.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Grape.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Grass.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Grey Green.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruber Darker.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruvbox Dark Hard.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruvbox Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruvbox Light Hard.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruvbox Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruvbox Material Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruvbox Material Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Gruvbox Material.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Guezwhoz.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/HaX0R Blue.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/HaX0R Gr33N.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/HaX0R R3D.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hacktober.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hardcore.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Harper.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Havn Daggry.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Havn Skumring.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Heeler.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Highway.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hipster Green.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hivacruz.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Homebrew.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hopscotch.256.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hopscotch.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Horizon Bright.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Horizon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hot Dog Stand (Mustard).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hot Dog Stand.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hurtado.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Hybrid.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/IBM 5153 CGA (Black).json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/IBM 5153 CGA.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/IC Green PPL.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/IC Orange PPL.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/IR Black.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/IRIX Console.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/IRIX Terminal.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Iceberg Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Iceberg Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Idea.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Idle Toes.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Jackie Brown.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Japanesque.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Jellybeans.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/JetBrains Darcula.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Jubi.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kanagawa Dragon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kanagawa Wave.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kanagawabones.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kibble.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kitty Default.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kitty Low Contrast.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kolorit.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Konsolas.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Kurokula.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Lab Fox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Laser.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Later This Evening.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Lavandula.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Light Owl.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Liquid Carbon Transparent.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Liquid Carbon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Lovelace.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Man Page.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Mariana.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Material Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Material Darker.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Material Design Colors.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Material Ocean.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Material.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Mathias.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Matrix.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Matte Black.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Medallion.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Melange Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Melange Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Mellifluous.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Mellow.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Miasma.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Midnight In Mojave.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Mirage.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Misterioso.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Molokai.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Mona Lisa.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Classic.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Pro Light Sun.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Pro Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Pro Machine.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Pro Octagon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Pro Ristretto.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Pro Spectrum.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Pro.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Remastered.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Soda.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Monokai Vivid.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Moonfly.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/N0Tch2K.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Neobones Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Neobones Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Neon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Neopolitan.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Neutron.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Night Lion V1.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Night Lion V2.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Night Owl.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Night Owlish Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nightfox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Niji.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/No Clown Fiesta Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/No Clown Fiesta.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nocturnal Winter.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nord Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nord Wave.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nord.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nordfox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Novel.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nvim Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Nvim Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Obsidian.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ocean.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Oceanic Material.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Oceanic Next.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ollie.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/One Dark Two.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/One Double Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/One Double Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/One Half Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/One Half Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Onenord Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Onenord.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Operator Mono Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Overnight Slumber.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Oxocarbon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Pale Night Hc.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Pandora.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Paraiso Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Paul Millr.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Pencil Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Pencil Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Peppermint.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Phala Green Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Piatto Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Pnevma.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Poimandres Darker.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Poimandres Storm.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Poimandres White.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Poimandres.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Popping And Locking.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Powershell.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Primary.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Pro Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Pro.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Purple Rain.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Purplepeter.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Rapture.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Raycast Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Raycast Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Rebecca.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Red Alert.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Red Planet.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Red Sands.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Relaxed.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Retro Legends.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Retro.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Rippedcasts.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Rose Pine Dawn.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Rose Pine Moon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Rose Pine.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Rouge 2.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Royal.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ryuuko.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Sakura.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Scarlet Protocol.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Sea Shells.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Seafoam Pastel.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Selenized Black.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Selenized Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Selenized Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Seoulbones Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Seoulbones Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Seti.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Shades Of Purple.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Shaman.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Slate.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Sleepy Hollow.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Smyck.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Snazzy Soft.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Snazzy.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Soft Server.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Solarized Darcula.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Solarized Dark Higher Contrast.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Solarized Dark Patched.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Solarized Osaka Night.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Sonokai.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Spacedust.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Spacegray Bright.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Spacegray Eighties Dull.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Spacegray Eighties.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Spacegray.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Spiderman.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Spring.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Square.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Squirrelsong Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Srcery.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Starlight.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Sublette.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Subliminal.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Sugarplum.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Sundried.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Symfonic.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Synthwave Alpha.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Synthwave Everything.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Synthwave.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tango Adapted.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tango Half Adapted.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tearout.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Teerb.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Terafox.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Terminal Basic Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Terminal Basic.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Thayer Bright.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/The Hulk.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tinacious Design Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tinacious Design Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/TokyoNight Day.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/TokyoNight Moon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/TokyoNight Night.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/TokyoNight Storm.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/TokyoNight.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tomorrow Night Blue.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tomorrow Night Bright.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tomorrow Night Burns.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tomorrow Night Eighties.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tomorrow Night.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Tomorrow.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Toy Chest.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Treehouse.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Twilight.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ubuntu.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ultra Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Ultra Violent.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Under The Sea.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Unikitty.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Urple.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Vague.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Vaughn.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Vercel.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Vesper.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Vibrant Ink.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Vimbones.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Violet Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Violet Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Violite.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Warm Neon.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Wez.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Whimsy.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Wild Cherry.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Wilmersdorf.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Wombat.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Wryan.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Xcode Dark hc.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Xcode Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Xcode Light hc.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Xcode Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Xcode WWDC.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Zenbones Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Zenbones Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Zenbones.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Zenburn.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Zenburned.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Zenwritten Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/Zenwritten Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/branch.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Dark Background.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Default.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Light Background.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Pastel Dark Background.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Smoooooth.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Solarized Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Solarized Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Tango Dark.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/iTerm2 Tango Light.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/novmbr.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/owl.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/traffic.json</file>
|
||||||
|
<file>KodoTermThemes/windowsterminal/urban.json</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
94
third_party/KodoTerm/KodoTermThemes/konsole/BlackOnLightYellow.colorscheme
vendored
Normal file
94
third_party/KodoTerm/KodoTermThemes/konsole/BlackOnLightYellow.colorscheme
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
[Background]
|
||||||
|
Color=255,255,221
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=255,255,221
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=255,255,221
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=192,192,192
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=178,24,24
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=255,84,84
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=224,142,142
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=142,224,142
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=224,224,142
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=24,24,178
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=142,142,224
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=178,24,178
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=224,142,224
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=24,178,178
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=84,255,255
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=142,224,224
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=178,178,178
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=142,142,142
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Black on Light Yellow
|
||||||
|
Opacity=1
|
||||||
104
third_party/KodoTerm/KodoTermThemes/konsole/BlackOnRandomLight.colorscheme
vendored
Normal file
104
third_party/KodoTerm/KodoTermThemes/konsole/BlackOnRandomLight.colorscheme
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
[Background]
|
||||||
|
Color=247,247,214
|
||||||
|
RandomHueRange=360
|
||||||
|
RandomSaturationRange=25
|
||||||
|
RandomLightnessRange=10
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=255,255,221
|
||||||
|
RandomHueRange=360
|
||||||
|
RandomSaturationRange=25
|
||||||
|
RandomLightnessRange=10
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=247,247,214
|
||||||
|
RandomHueRange=360
|
||||||
|
RandomSaturationRange=25
|
||||||
|
RandomLightnessRange=10
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=192,192,192
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=178,24,24
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=255,84,84
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=224,142,142
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=142,224,142
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=224,224,142
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=24,24,178
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=142,142,224
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=178,24,178
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=224,142,224
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=24,178,178
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=84,255,255
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=142,224,224
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=178,178,178
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=142,142,142
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Black on Random Light
|
||||||
|
ColorRandomization=true
|
||||||
|
Opacity=1
|
||||||
94
third_party/KodoTerm/KodoTermThemes/konsole/BlackOnWhite.colorscheme
vendored
Normal file
94
third_party/KodoTerm/KodoTermThemes/konsole/BlackOnWhite.colorscheme
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
[Background]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=192,192,192
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=178,24,24
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=255,84,84
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=224,142,142
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=142,224,142
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=224,224,142
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=24,24,178
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=142,142,224
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=178,24,178
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=224,142,224
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=24,178,178
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=84,255,255
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=142,224,224
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=178,178,178
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=142,142,142
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Black on White
|
||||||
|
Opacity=1
|
||||||
94
third_party/KodoTerm/KodoTermThemes/konsole/BlueOnBlack.colorscheme
vendored
Normal file
94
third_party/KodoTerm/KodoTermThemes/konsole/BlueOnBlack.colorscheme
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
[Background]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=192,192,192
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=250,0,0
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=75,93,255
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=250,0,0
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=142,224,142
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=224,224,142
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=125,152,35
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=125,152,35
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=225,30,225
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=175,29,175
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=0,134,223
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=0,68,255
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=0,98,173
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=50,50,50
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=200,200,200
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=0,119,255
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=23,74,240
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=0,90,195
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Blue on Black
|
||||||
|
Opacity=1
|
||||||
94
third_party/KodoTerm/KodoTermThemes/konsole/Breeze.colorscheme
vendored
Normal file
94
third_party/KodoTerm/KodoTermThemes/konsole/Breeze.colorscheme
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
[Background]
|
||||||
|
Color=35,38,39
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=49,54,59
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=35,38,39
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=49,54,59
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=127,140,141
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=237,21,21
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=120,50,40
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=192,57,43
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=17,209,22
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=23,162,98
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=28,220,154
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=246,116,0
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=182,86,25
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=253,188,75
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=29,153,243
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=27,102,143
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=61,174,233
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=155,89,182
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=97,74,115
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=142,68,173
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=26,188,156
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=24,108,96
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=22,160,133
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=252,252,252
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=99,104,109
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=252,252,252
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=239,240,241
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Color=61,174,233
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Breeze
|
||||||
|
Opacity=1
|
||||||
|
Wallpaper=
|
||||||
100
third_party/KodoTerm/KodoTermThemes/konsole/Campbell.colorscheme
vendored
Normal file
100
third_party/KodoTerm/KodoTermThemes/konsole/Campbell.colorscheme
vendored
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
[Background]
|
||||||
|
Color=12,12,12
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=44,44,44
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=12,12,12
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=44,44,44
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=124,124,124
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=197,15,31
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=150,11,25
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=231,72,86
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=19,161,14
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=16,120,10
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=22,198,12
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=193,156,0
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=150,120,0
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=249,241,165
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=0,55,218
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=0,45,170
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=59,120,255
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=136,23,152
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=106,18,120
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=180,0,158
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=58,150,221
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=47,122,180
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=97,214,214
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=204,204,204
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=170,170,170
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=242,242,242
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=204,204,204
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=170,170,170
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Anchor=0.5,0.5
|
||||||
|
Blur=false
|
||||||
|
ColorRandomization=false
|
||||||
|
Description=Campbell
|
||||||
|
FillStyle=Tile
|
||||||
|
Opacity=1
|
||||||
|
Wallpaper=
|
||||||
|
WallpaperFlipType=NoFlip
|
||||||
|
WallpaperOpacity=1
|
||||||
103
third_party/KodoTerm/KodoTermThemes/konsole/DarkPastels.colorscheme
vendored
Normal file
103
third_party/KodoTerm/KodoTermThemes/konsole/DarkPastels.colorscheme
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
[Background]
|
||||||
|
Color=44,44,44
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=44,44,44
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=44,44,44
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=63,63,63
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=112,144,128
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=52,52,52
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=112,80,80
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=220,163,163
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=102,72,72
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=96,180,138
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=114,213,163
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=87,163,124
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=223,175,143
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=240,223,175
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=170,133,111
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=154,184,215
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=148,191,243
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=117,141,161
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=220,140,195
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=236,147,211
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=154,98,137
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=140,208,211
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=147,224,227
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=107,159,161
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=220,220,204
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Bold=true
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=149,149,139
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=220,220,204
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=220,220,204
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=220,220,204
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Dark Pastels
|
||||||
|
Opacity=1
|
||||||
94
third_party/KodoTerm/KodoTermThemes/konsole/GreenOnBlack.colorscheme
vendored
Normal file
94
third_party/KodoTerm/KodoTermThemes/konsole/GreenOnBlack.colorscheme
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
[Background]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=24,24,24
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=250,75,75
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=255,84,84
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=101,25,25
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=0,101,0
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=101,74,0
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=24,24,178
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=0,0,101
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=225,30,225
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=95,5,95
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=24,178,178
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=84,255,255
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=0,101,101
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=178,178,178
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=101,101,101
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=24,240,24
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=24,240,24
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=18,200,18
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Green on Black
|
||||||
|
Opacity=1
|
||||||
92
third_party/KodoTerm/KodoTermThemes/konsole/Linux.colorscheme
vendored
Normal file
92
third_party/KodoTerm/KodoTermThemes/konsole/Linux.colorscheme
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
[Background]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=24,24,24
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=178,24,24
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=255,84,84
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=101,0,0
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=0,101,0
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=101,94,0
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=24,24,178
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=0,0,101
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=178,24,178
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=101,0,101
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=24,178,178
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=84,255,255
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=0,101,101
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=178,178,178
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=101,101,101
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=178,178,178
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=101,101,101
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Linux Colors
|
||||||
94
third_party/KodoTerm/KodoTermThemes/konsole/RedOnBlack.colorscheme
vendored
Normal file
94
third_party/KodoTerm/KodoTermThemes/konsole/RedOnBlack.colorscheme
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
[Background]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=24,24,24
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=250,142,8
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=255,84,84
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=101,25,0
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=0,101,0
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=101,74,0
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=30,71,152
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=0,24,102
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=225,30,225
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=95,5,95
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=0,134,223
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=255,0,4
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=0,94,163
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=50,50,50
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=101,101,101
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=255,0,0
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=24,240,24
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=205,0,0
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Red on Black
|
||||||
|
Opacity=1
|
||||||
93
third_party/KodoTerm/KodoTermThemes/konsole/Solarized.colorscheme
vendored
Normal file
93
third_party/KodoTerm/KodoTermThemes/konsole/Solarized.colorscheme
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
[Color0]
|
||||||
|
Color=7,54,66
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=0,43,54
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=6,48,59
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=220,50,47
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=203,75,22
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=147,33,31
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=133,153,0
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=88,110,117
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=94,106,0
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=181,137,0
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=101,123,131
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=138,103,0
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=38,139,210
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=131,148,150
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=20,77,115
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=211,54,130
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=108,113,196
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=120,30,75
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=42,161,152
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=147,161,161
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=24,94,88
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=238,232,213
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=253,246,227
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=171,167,154
|
||||||
|
|
||||||
|
[Background]
|
||||||
|
Color=0,43,54
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=7,54,66
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=0,43,54
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=131,148,150
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Color=147,161,161
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=106,119,121
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Solarized
|
||||||
|
Opacity=1
|
||||||
93
third_party/KodoTerm/KodoTermThemes/konsole/SolarizedLight.colorscheme
vendored
Normal file
93
third_party/KodoTerm/KodoTermThemes/konsole/SolarizedLight.colorscheme
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
[Color0]
|
||||||
|
Color=7,54,66
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=0,43,54
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=8,65,80
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=220,50,47
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=203,75,22
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=222,81,81
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=133,153,0
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=88,110,117
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=153,168,39
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=181,137,0
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=101,123,131
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=213,170,49
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=38,139,210
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=131,148,150
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=80,173,226
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=211,54,130
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=108,113,196
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=223,92,158
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=42,161,152
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=147,161,161
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=78,211,200
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=238,232,213
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=253,246,227
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=238,232,213
|
||||||
|
|
||||||
|
[Background]
|
||||||
|
Color=253,246,227
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=238,232,213
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=253,246,227
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=101,123,131
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Color=88,110,117
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=141,172,182
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=Solarized Light
|
||||||
|
Opacity=1
|
||||||
94
third_party/KodoTerm/KodoTermThemes/konsole/WhiteOnBlack.colorscheme
vendored
Normal file
94
third_party/KodoTerm/KodoTermThemes/konsole/WhiteOnBlack.colorscheme
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
[Background]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundIntense]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[BackgroundFaint]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0]
|
||||||
|
Color=0,0,0
|
||||||
|
|
||||||
|
[Color0Intense]
|
||||||
|
Color=104,104,104
|
||||||
|
|
||||||
|
[Color0Faint]
|
||||||
|
Color=24,24,24
|
||||||
|
|
||||||
|
[Color1]
|
||||||
|
Color=178,24,24
|
||||||
|
|
||||||
|
[Color1Intense]
|
||||||
|
Color=255,84,84
|
||||||
|
|
||||||
|
[Color1Faint]
|
||||||
|
Color=101,0,0
|
||||||
|
|
||||||
|
[Color2]
|
||||||
|
Color=24,178,24
|
||||||
|
|
||||||
|
[Color2Intense]
|
||||||
|
Color=84,255,84
|
||||||
|
|
||||||
|
[Color2Faint]
|
||||||
|
Color=0,101,0
|
||||||
|
|
||||||
|
[Color3]
|
||||||
|
Color=178,104,24
|
||||||
|
|
||||||
|
[Color3Intense]
|
||||||
|
Color=255,255,84
|
||||||
|
|
||||||
|
[Color3Faint]
|
||||||
|
Color=101,74,0
|
||||||
|
|
||||||
|
[Color4]
|
||||||
|
Color=24,24,178
|
||||||
|
|
||||||
|
[Color4Intense]
|
||||||
|
Color=84,84,255
|
||||||
|
|
||||||
|
[Color4Faint]
|
||||||
|
Color=0,0,101
|
||||||
|
|
||||||
|
[Color5]
|
||||||
|
Color=178,24,178
|
||||||
|
|
||||||
|
[Color5Intense]
|
||||||
|
Color=255,84,255
|
||||||
|
|
||||||
|
[Color5Faint]
|
||||||
|
Color=95,5,95
|
||||||
|
|
||||||
|
[Color6]
|
||||||
|
Color=24,178,178
|
||||||
|
|
||||||
|
[Color6Intense]
|
||||||
|
Color=84,255,255
|
||||||
|
|
||||||
|
[Color6Faint]
|
||||||
|
Color=24,178,178
|
||||||
|
|
||||||
|
[Color7]
|
||||||
|
Color=178,178,178
|
||||||
|
|
||||||
|
[Color7Intense]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[Color7Faint]
|
||||||
|
Color=101,101,101
|
||||||
|
|
||||||
|
[Foreground]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[ForegroundIntense]
|
||||||
|
Bold=true
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[ForegroundFaint]
|
||||||
|
Color=255,255,255
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Description=White on Black
|
||||||
|
Opacity=1
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/0x96f.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/0x96f.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "0x96f",
|
||||||
|
"black": "#262427",
|
||||||
|
"red": "#ff666d",
|
||||||
|
"green": "#b3e03a",
|
||||||
|
"yellow": "#ffc739",
|
||||||
|
"blue": "#00cde8",
|
||||||
|
"purple": "#a392e8",
|
||||||
|
"cyan": "#9deaf6",
|
||||||
|
"white": "#fcfcfa",
|
||||||
|
"brightBlack": "#545452",
|
||||||
|
"brightRed": "#ff7e83",
|
||||||
|
"brightGreen": "#bee55e",
|
||||||
|
"brightYellow": "#ffd05e",
|
||||||
|
"brightBlue": "#1bd5eb",
|
||||||
|
"brightPurple": "#b0a3eb",
|
||||||
|
"brightCyan": "#acedf8",
|
||||||
|
"brightWhite": "#fcfcfa",
|
||||||
|
"background": "#262427",
|
||||||
|
"foreground": "#fcfcfa",
|
||||||
|
"cursorColor": "#fcfcfa",
|
||||||
|
"selectionBackground": "#fcfcfa"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/12-bit Rainbow.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/12-bit Rainbow.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "12-bit Rainbow",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#a03050",
|
||||||
|
"green": "#40d080",
|
||||||
|
"yellow": "#e09040",
|
||||||
|
"blue": "#3060b0",
|
||||||
|
"purple": "#603090",
|
||||||
|
"cyan": "#0090c0",
|
||||||
|
"white": "#dbded8",
|
||||||
|
"brightBlack": "#685656",
|
||||||
|
"brightRed": "#c06060",
|
||||||
|
"brightGreen": "#90d050",
|
||||||
|
"brightYellow": "#e0d000",
|
||||||
|
"brightBlue": "#00b0c0",
|
||||||
|
"brightPurple": "#801070",
|
||||||
|
"brightCyan": "#20b0c0",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#040404",
|
||||||
|
"foreground": "#feffff",
|
||||||
|
"cursorColor": "#e0d000",
|
||||||
|
"selectionBackground": "#606060"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/3024 Day.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/3024 Day.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "3024 Day",
|
||||||
|
"black": "#090300",
|
||||||
|
"red": "#db2d20",
|
||||||
|
"green": "#01a252",
|
||||||
|
"yellow": "#caba00",
|
||||||
|
"blue": "#01a0e4",
|
||||||
|
"purple": "#a16a94",
|
||||||
|
"cyan": "#8fbece",
|
||||||
|
"white": "#a5a2a2",
|
||||||
|
"brightBlack": "#5c5855",
|
||||||
|
"brightRed": "#dbaec3",
|
||||||
|
"brightGreen": "#3a3432",
|
||||||
|
"brightYellow": "#4a4543",
|
||||||
|
"brightBlue": "#807d7c",
|
||||||
|
"brightPurple": "#bcbbba",
|
||||||
|
"brightCyan": "#cdab53",
|
||||||
|
"brightWhite": "#f7f7f7",
|
||||||
|
"background": "#f7f7f7",
|
||||||
|
"foreground": "#4a4543",
|
||||||
|
"cursorColor": "#4a4543",
|
||||||
|
"selectionBackground": "#a5a2a2"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/3024 Night.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/3024 Night.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "3024 Night",
|
||||||
|
"black": "#090300",
|
||||||
|
"red": "#db2d20",
|
||||||
|
"green": "#01a252",
|
||||||
|
"yellow": "#fded02",
|
||||||
|
"blue": "#01a0e4",
|
||||||
|
"purple": "#a16a94",
|
||||||
|
"cyan": "#b5e4f4",
|
||||||
|
"white": "#a5a2a2",
|
||||||
|
"brightBlack": "#5c5855",
|
||||||
|
"brightRed": "#e8bbd0",
|
||||||
|
"brightGreen": "#47413f",
|
||||||
|
"brightYellow": "#4a4543",
|
||||||
|
"brightBlue": "#807d7c",
|
||||||
|
"brightPurple": "#d6d5d4",
|
||||||
|
"brightCyan": "#cdab53",
|
||||||
|
"brightWhite": "#f7f7f7",
|
||||||
|
"background": "#090300",
|
||||||
|
"foreground": "#a5a2a2",
|
||||||
|
"cursorColor": "#a5a2a2",
|
||||||
|
"selectionBackground": "#4a4543"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aardvark Blue.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aardvark Blue.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Aardvark Blue",
|
||||||
|
"black": "#191919",
|
||||||
|
"red": "#aa342e",
|
||||||
|
"green": "#4b8c0f",
|
||||||
|
"yellow": "#dbba00",
|
||||||
|
"blue": "#1370d3",
|
||||||
|
"purple": "#c43ac3",
|
||||||
|
"cyan": "#008eb0",
|
||||||
|
"white": "#bebebe",
|
||||||
|
"brightBlack": "#525252",
|
||||||
|
"brightRed": "#f05b50",
|
||||||
|
"brightGreen": "#95dc55",
|
||||||
|
"brightYellow": "#ffe763",
|
||||||
|
"brightBlue": "#60a4ec",
|
||||||
|
"brightPurple": "#e26be2",
|
||||||
|
"brightCyan": "#60b6cb",
|
||||||
|
"brightWhite": "#f7f7f7",
|
||||||
|
"background": "#102040",
|
||||||
|
"foreground": "#dddddd",
|
||||||
|
"cursorColor": "#007acc",
|
||||||
|
"selectionBackground": "#bfdbfe"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Abernathy.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Abernathy.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Abernathy",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#cd0000",
|
||||||
|
"green": "#00cd00",
|
||||||
|
"yellow": "#cdcd00",
|
||||||
|
"blue": "#1093f5",
|
||||||
|
"purple": "#cd00cd",
|
||||||
|
"cyan": "#00cdcd",
|
||||||
|
"white": "#faebd7",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#ff0000",
|
||||||
|
"brightGreen": "#00ff00",
|
||||||
|
"brightYellow": "#ffff00",
|
||||||
|
"brightBlue": "#11b5f6",
|
||||||
|
"brightPurple": "#ff00ff",
|
||||||
|
"brightCyan": "#00ffff",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#111416",
|
||||||
|
"foreground": "#eeeeec",
|
||||||
|
"cursorColor": "#bbbbbb",
|
||||||
|
"selectionBackground": "#eeeeec"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adventure Time.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adventure Time.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Adventure Time",
|
||||||
|
"black": "#050404",
|
||||||
|
"red": "#bd0013",
|
||||||
|
"green": "#4ab118",
|
||||||
|
"yellow": "#e7741e",
|
||||||
|
"blue": "#0f4ac6",
|
||||||
|
"purple": "#665993",
|
||||||
|
"cyan": "#70a598",
|
||||||
|
"white": "#f8dcc0",
|
||||||
|
"brightBlack": "#4e7cbf",
|
||||||
|
"brightRed": "#fc5f5a",
|
||||||
|
"brightGreen": "#9eff6e",
|
||||||
|
"brightYellow": "#efc11a",
|
||||||
|
"brightBlue": "#1997c6",
|
||||||
|
"brightPurple": "#9b5953",
|
||||||
|
"brightCyan": "#c8faf4",
|
||||||
|
"brightWhite": "#f6f5fb",
|
||||||
|
"background": "#1f1d45",
|
||||||
|
"foreground": "#f8dcc0",
|
||||||
|
"cursorColor": "#efbf38",
|
||||||
|
"selectionBackground": "#706b4e"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adventure.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adventure.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Adventure",
|
||||||
|
"black": "#040404",
|
||||||
|
"red": "#d84a33",
|
||||||
|
"green": "#5da602",
|
||||||
|
"yellow": "#eebb6e",
|
||||||
|
"blue": "#417ab3",
|
||||||
|
"purple": "#e5c499",
|
||||||
|
"cyan": "#bdcfe5",
|
||||||
|
"white": "#dbded8",
|
||||||
|
"brightBlack": "#685656",
|
||||||
|
"brightRed": "#d76b42",
|
||||||
|
"brightGreen": "#99b52c",
|
||||||
|
"brightYellow": "#ffb670",
|
||||||
|
"brightBlue": "#97d7ef",
|
||||||
|
"brightPurple": "#aa7900",
|
||||||
|
"brightCyan": "#bdcfe5",
|
||||||
|
"brightWhite": "#e4d5c7",
|
||||||
|
"background": "#040404",
|
||||||
|
"foreground": "#feffff",
|
||||||
|
"cursorColor": "#feffff",
|
||||||
|
"selectionBackground": "#606060"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adwaita Dark.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adwaita Dark.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Adwaita Dark",
|
||||||
|
"black": "#241f31",
|
||||||
|
"red": "#c01c28",
|
||||||
|
"green": "#2ec27e",
|
||||||
|
"yellow": "#f5c211",
|
||||||
|
"blue": "#1e78e4",
|
||||||
|
"purple": "#9841bb",
|
||||||
|
"cyan": "#0ab9dc",
|
||||||
|
"white": "#c0bfbc",
|
||||||
|
"brightBlack": "#5e5c64",
|
||||||
|
"brightRed": "#ed333b",
|
||||||
|
"brightGreen": "#57e389",
|
||||||
|
"brightYellow": "#f8e45c",
|
||||||
|
"brightBlue": "#51a1ff",
|
||||||
|
"brightPurple": "#c061cb",
|
||||||
|
"brightCyan": "#4fd2fd",
|
||||||
|
"brightWhite": "#f6f5f4",
|
||||||
|
"background": "#1d1d20",
|
||||||
|
"foreground": "#ffffff",
|
||||||
|
"cursorColor": "#ffffff",
|
||||||
|
"selectionBackground": "#ffffff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adwaita.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Adwaita.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Adwaita",
|
||||||
|
"black": "#241f31",
|
||||||
|
"red": "#c01c28",
|
||||||
|
"green": "#2ec27e",
|
||||||
|
"yellow": "#e8b504",
|
||||||
|
"blue": "#1e78e4",
|
||||||
|
"purple": "#9841bb",
|
||||||
|
"cyan": "#0ab9dc",
|
||||||
|
"white": "#c0bfbc",
|
||||||
|
"brightBlack": "#5e5c64",
|
||||||
|
"brightRed": "#ed333b",
|
||||||
|
"brightGreen": "#4ad67c",
|
||||||
|
"brightYellow": "#d2be36",
|
||||||
|
"brightBlue": "#51a1ff",
|
||||||
|
"brightPurple": "#c061cb",
|
||||||
|
"brightCyan": "#4fd2fd",
|
||||||
|
"brightWhite": "#f6f5f4",
|
||||||
|
"background": "#ffffff",
|
||||||
|
"foreground": "#000000",
|
||||||
|
"cursorColor": "#000000",
|
||||||
|
"selectionBackground": "#c0bfbc"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Afterglow.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Afterglow.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Afterglow",
|
||||||
|
"black": "#151515",
|
||||||
|
"red": "#ac4142",
|
||||||
|
"green": "#7e8e50",
|
||||||
|
"yellow": "#e5b567",
|
||||||
|
"blue": "#6c99bb",
|
||||||
|
"purple": "#9f4e85",
|
||||||
|
"cyan": "#7dd6cf",
|
||||||
|
"white": "#d0d0d0",
|
||||||
|
"brightBlack": "#505050",
|
||||||
|
"brightRed": "#ac4142",
|
||||||
|
"brightGreen": "#7e8e50",
|
||||||
|
"brightYellow": "#e5b567",
|
||||||
|
"brightBlue": "#6c99bb",
|
||||||
|
"brightPurple": "#9f4e85",
|
||||||
|
"brightCyan": "#7dd6cf",
|
||||||
|
"brightWhite": "#f5f5f5",
|
||||||
|
"background": "#212121",
|
||||||
|
"foreground": "#d0d0d0",
|
||||||
|
"cursorColor": "#d0d0d0",
|
||||||
|
"selectionBackground": "#303030"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aizen Dark.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aizen Dark.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Aizen Dark",
|
||||||
|
"black": "#1a1a1a",
|
||||||
|
"red": "#f08898",
|
||||||
|
"green": "#a4e09c",
|
||||||
|
"yellow": "#f5dea4",
|
||||||
|
"blue": "#84b4f8",
|
||||||
|
"purple": "#c8a2f4",
|
||||||
|
"cyan": "#90dcd0",
|
||||||
|
"white": "#d0d6f0",
|
||||||
|
"brightBlack": "#444444",
|
||||||
|
"brightRed": "#f08898",
|
||||||
|
"brightGreen": "#a4e09c",
|
||||||
|
"brightYellow": "#f5dea4",
|
||||||
|
"brightBlue": "#84b4f8",
|
||||||
|
"brightPurple": "#c8a2f4",
|
||||||
|
"brightCyan": "#90dcd0",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#1a1a1a",
|
||||||
|
"foreground": "#d0d6f0",
|
||||||
|
"cursorColor": "#f8b080",
|
||||||
|
"selectionBackground": "#333333"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aizen Light.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aizen Light.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Aizen Light",
|
||||||
|
"black": "#f0f2f6",
|
||||||
|
"red": "#d00c36",
|
||||||
|
"green": "#3e9e28",
|
||||||
|
"yellow": "#dd8c1a",
|
||||||
|
"blue": "#1c64f2",
|
||||||
|
"purple": "#8636ec",
|
||||||
|
"cyan": "#159096",
|
||||||
|
"white": "#4a4d66",
|
||||||
|
"brightBlack": "#adb2bc",
|
||||||
|
"brightRed": "#d00c36",
|
||||||
|
"brightGreen": "#3e9e28",
|
||||||
|
"brightYellow": "#dd8c1a",
|
||||||
|
"brightBlue": "#1c64f2",
|
||||||
|
"brightPurple": "#8636ec",
|
||||||
|
"brightCyan": "#159096",
|
||||||
|
"brightWhite": "#4a4d66",
|
||||||
|
"background": "#f0f2f6",
|
||||||
|
"foreground": "#4a4d66",
|
||||||
|
"cursorColor": "#fc6008",
|
||||||
|
"selectionBackground": "#bdc2cc"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Alabaster.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Alabaster.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Alabaster",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#aa3731",
|
||||||
|
"green": "#448c27",
|
||||||
|
"yellow": "#cb9000",
|
||||||
|
"blue": "#325cc0",
|
||||||
|
"purple": "#7a3e9d",
|
||||||
|
"cyan": "#0083b2",
|
||||||
|
"white": "#b7b7b7",
|
||||||
|
"brightBlack": "#777777",
|
||||||
|
"brightRed": "#f05050",
|
||||||
|
"brightGreen": "#60cb00",
|
||||||
|
"brightYellow": "#f2af50",
|
||||||
|
"brightBlue": "#007acc",
|
||||||
|
"brightPurple": "#e64ce6",
|
||||||
|
"brightCyan": "#00aacb",
|
||||||
|
"brightWhite": "#f7f7f7",
|
||||||
|
"background": "#f7f7f7",
|
||||||
|
"foreground": "#000000",
|
||||||
|
"cursorColor": "#007acc",
|
||||||
|
"selectionBackground": "#bfdbfe"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Alien Blood.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Alien Blood.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Alien Blood",
|
||||||
|
"black": "#112616",
|
||||||
|
"red": "#7f2b27",
|
||||||
|
"green": "#2f7e25",
|
||||||
|
"yellow": "#717f24",
|
||||||
|
"blue": "#2f6a7f",
|
||||||
|
"purple": "#47587f",
|
||||||
|
"cyan": "#327f77",
|
||||||
|
"white": "#647d75",
|
||||||
|
"brightBlack": "#3c4812",
|
||||||
|
"brightRed": "#e08009",
|
||||||
|
"brightGreen": "#18e000",
|
||||||
|
"brightYellow": "#bde000",
|
||||||
|
"brightBlue": "#00aae0",
|
||||||
|
"brightPurple": "#0058e0",
|
||||||
|
"brightCyan": "#00e0c4",
|
||||||
|
"brightWhite": "#73fa91",
|
||||||
|
"background": "#0f1610",
|
||||||
|
"foreground": "#637d75",
|
||||||
|
"cursorColor": "#73fa91",
|
||||||
|
"selectionBackground": "#1d4125"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Andromeda.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Andromeda.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Andromeda",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#cd3131",
|
||||||
|
"green": "#05bc79",
|
||||||
|
"yellow": "#e5e512",
|
||||||
|
"blue": "#2472c8",
|
||||||
|
"purple": "#bc3fbc",
|
||||||
|
"cyan": "#0fa8cd",
|
||||||
|
"white": "#e5e5e5",
|
||||||
|
"brightBlack": "#666666",
|
||||||
|
"brightRed": "#cd3131",
|
||||||
|
"brightGreen": "#05bc79",
|
||||||
|
"brightYellow": "#e5e512",
|
||||||
|
"brightBlue": "#2472c8",
|
||||||
|
"brightPurple": "#bc3fbc",
|
||||||
|
"brightCyan": "#0fa8cd",
|
||||||
|
"brightWhite": "#e5e5e5",
|
||||||
|
"background": "#262a33",
|
||||||
|
"foreground": "#e5e5e5",
|
||||||
|
"cursorColor": "#f8f8f0",
|
||||||
|
"selectionBackground": "#5a5c62"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Apple Classic.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Apple Classic.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Apple Classic",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#c91b00",
|
||||||
|
"green": "#00c200",
|
||||||
|
"yellow": "#c7c400",
|
||||||
|
"blue": "#1c3fe1",
|
||||||
|
"purple": "#ca30c7",
|
||||||
|
"cyan": "#00c5c7",
|
||||||
|
"white": "#c7c7c7",
|
||||||
|
"brightBlack": "#686868",
|
||||||
|
"brightRed": "#ff6e67",
|
||||||
|
"brightGreen": "#5ffa68",
|
||||||
|
"brightYellow": "#fffc67",
|
||||||
|
"brightBlue": "#6871ff",
|
||||||
|
"brightPurple": "#ff77ff",
|
||||||
|
"brightCyan": "#60fdff",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#2c2b2b",
|
||||||
|
"foreground": "#d5a200",
|
||||||
|
"cursorColor": "#c7c7c7",
|
||||||
|
"selectionBackground": "#6b5b02"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Apple System Colors Light.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Apple System Colors Light.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Apple System Colors Light",
|
||||||
|
"black": "#1a1a1a",
|
||||||
|
"red": "#cc372e",
|
||||||
|
"green": "#26a439",
|
||||||
|
"yellow": "#cdac08",
|
||||||
|
"blue": "#0869cb",
|
||||||
|
"purple": "#9647bf",
|
||||||
|
"cyan": "#479ec2",
|
||||||
|
"white": "#98989d",
|
||||||
|
"brightBlack": "#464646",
|
||||||
|
"brightRed": "#ff453a",
|
||||||
|
"brightGreen": "#32d74b",
|
||||||
|
"brightYellow": "#e5bc00",
|
||||||
|
"brightBlue": "#0a84ff",
|
||||||
|
"brightPurple": "#bf5af2",
|
||||||
|
"brightCyan": "#69c9f2",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#feffff",
|
||||||
|
"foreground": "#000000",
|
||||||
|
"cursorColor": "#98989d",
|
||||||
|
"selectionBackground": "#abd8ff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Apple System Colors.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Apple System Colors.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Apple System Colors",
|
||||||
|
"black": "#1a1a1a",
|
||||||
|
"red": "#cc372e",
|
||||||
|
"green": "#26a439",
|
||||||
|
"yellow": "#cdac08",
|
||||||
|
"blue": "#0869cb",
|
||||||
|
"purple": "#9647bf",
|
||||||
|
"cyan": "#479ec2",
|
||||||
|
"white": "#98989d",
|
||||||
|
"brightBlack": "#464646",
|
||||||
|
"brightRed": "#ff453a",
|
||||||
|
"brightGreen": "#32d74b",
|
||||||
|
"brightYellow": "#ffd60a",
|
||||||
|
"brightBlue": "#0a84ff",
|
||||||
|
"brightPurple": "#bf5af2",
|
||||||
|
"brightCyan": "#76d6ff",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#1e1e1e",
|
||||||
|
"foreground": "#ffffff",
|
||||||
|
"cursorColor": "#98989d",
|
||||||
|
"selectionBackground": "#3f638b"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Arcoiris.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Arcoiris.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Arcoiris",
|
||||||
|
"black": "#333333",
|
||||||
|
"red": "#da2700",
|
||||||
|
"green": "#12c258",
|
||||||
|
"yellow": "#ffc656",
|
||||||
|
"blue": "#518bfc",
|
||||||
|
"purple": "#e37bd9",
|
||||||
|
"cyan": "#63fad5",
|
||||||
|
"white": "#bab2b2",
|
||||||
|
"brightBlack": "#777777",
|
||||||
|
"brightRed": "#ffb9b9",
|
||||||
|
"brightGreen": "#e3f6aa",
|
||||||
|
"brightYellow": "#ffddaa",
|
||||||
|
"brightBlue": "#b3e8f3",
|
||||||
|
"brightPurple": "#cbbaf9",
|
||||||
|
"brightCyan": "#bcffc7",
|
||||||
|
"brightWhite": "#efefef",
|
||||||
|
"background": "#201f1e",
|
||||||
|
"foreground": "#eee4d9",
|
||||||
|
"cursorColor": "#872929",
|
||||||
|
"selectionBackground": "#25524a"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ardoise.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ardoise.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Ardoise",
|
||||||
|
"black": "#2c2c2c",
|
||||||
|
"red": "#d3322d",
|
||||||
|
"green": "#588b35",
|
||||||
|
"yellow": "#fca93a",
|
||||||
|
"blue": "#2465c2",
|
||||||
|
"purple": "#7332b4",
|
||||||
|
"cyan": "#64e1b8",
|
||||||
|
"white": "#f7f7f7",
|
||||||
|
"brightBlack": "#535353",
|
||||||
|
"brightRed": "#fa5852",
|
||||||
|
"brightGreen": "#8dc252",
|
||||||
|
"brightYellow": "#ffea51",
|
||||||
|
"brightBlue": "#6ab5f8",
|
||||||
|
"brightPurple": "#be68ca",
|
||||||
|
"brightCyan": "#89ffdb",
|
||||||
|
"brightWhite": "#fefefe",
|
||||||
|
"background": "#1e1e1e",
|
||||||
|
"foreground": "#eaeaea",
|
||||||
|
"cursorColor": "#f7f7f7",
|
||||||
|
"selectionBackground": "#46515e"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Argonaut.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Argonaut.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Argonaut",
|
||||||
|
"black": "#232323",
|
||||||
|
"red": "#ff000f",
|
||||||
|
"green": "#8ce10b",
|
||||||
|
"yellow": "#ffb900",
|
||||||
|
"blue": "#008df8",
|
||||||
|
"purple": "#6d43a6",
|
||||||
|
"cyan": "#00d8eb",
|
||||||
|
"white": "#ffffff",
|
||||||
|
"brightBlack": "#444444",
|
||||||
|
"brightRed": "#ff2740",
|
||||||
|
"brightGreen": "#abe15b",
|
||||||
|
"brightYellow": "#ffd242",
|
||||||
|
"brightBlue": "#0092ff",
|
||||||
|
"brightPurple": "#9a5feb",
|
||||||
|
"brightCyan": "#67fff0",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#0e1019",
|
||||||
|
"foreground": "#fffaf4",
|
||||||
|
"cursorColor": "#ff0018",
|
||||||
|
"selectionBackground": "#002a3b"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Arthur.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Arthur.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Arthur",
|
||||||
|
"black": "#3d352a",
|
||||||
|
"red": "#cd5c5c",
|
||||||
|
"green": "#86af80",
|
||||||
|
"yellow": "#e8ae5b",
|
||||||
|
"blue": "#6495ed",
|
||||||
|
"purple": "#deb887",
|
||||||
|
"cyan": "#b0c4de",
|
||||||
|
"white": "#bbaa99",
|
||||||
|
"brightBlack": "#554444",
|
||||||
|
"brightRed": "#cc5533",
|
||||||
|
"brightGreen": "#88aa22",
|
||||||
|
"brightYellow": "#ffa75d",
|
||||||
|
"brightBlue": "#87ceeb",
|
||||||
|
"brightPurple": "#996600",
|
||||||
|
"brightCyan": "#b0c4de",
|
||||||
|
"brightWhite": "#ddccbb",
|
||||||
|
"background": "#1c1c1c",
|
||||||
|
"foreground": "#ddeedd",
|
||||||
|
"cursorColor": "#e2bbef",
|
||||||
|
"selectionBackground": "#4d4d4d"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atelier Sulphurpool.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atelier Sulphurpool.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Atelier Sulphurpool",
|
||||||
|
"black": "#202746",
|
||||||
|
"red": "#c94922",
|
||||||
|
"green": "#ac9739",
|
||||||
|
"yellow": "#c08b30",
|
||||||
|
"blue": "#3d8fd1",
|
||||||
|
"purple": "#6679cc",
|
||||||
|
"cyan": "#22a2c9",
|
||||||
|
"white": "#979db4",
|
||||||
|
"brightBlack": "#6b7394",
|
||||||
|
"brightRed": "#c76b29",
|
||||||
|
"brightGreen": "#4f587c",
|
||||||
|
"brightYellow": "#5e6687",
|
||||||
|
"brightBlue": "#898ea4",
|
||||||
|
"brightPurple": "#dfe2f1",
|
||||||
|
"brightCyan": "#9c637a",
|
||||||
|
"brightWhite": "#f5f7ff",
|
||||||
|
"background": "#202746",
|
||||||
|
"foreground": "#979db4",
|
||||||
|
"cursorColor": "#979db4",
|
||||||
|
"selectionBackground": "#5e6687"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atom One Dark.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atom One Dark.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Atom One Dark",
|
||||||
|
"black": "#21252b",
|
||||||
|
"red": "#e06c75",
|
||||||
|
"green": "#98c379",
|
||||||
|
"yellow": "#e5c07b",
|
||||||
|
"blue": "#61afef",
|
||||||
|
"purple": "#c678dd",
|
||||||
|
"cyan": "#56b6c2",
|
||||||
|
"white": "#abb2bf",
|
||||||
|
"brightBlack": "#767676",
|
||||||
|
"brightRed": "#e06c75",
|
||||||
|
"brightGreen": "#98c379",
|
||||||
|
"brightYellow": "#e5c07b",
|
||||||
|
"brightBlue": "#61afef",
|
||||||
|
"brightPurple": "#c678dd",
|
||||||
|
"brightCyan": "#56b6c2",
|
||||||
|
"brightWhite": "#abb2bf",
|
||||||
|
"background": "#21252b",
|
||||||
|
"foreground": "#abb2bf",
|
||||||
|
"cursorColor": "#abb2bf",
|
||||||
|
"selectionBackground": "#323844"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atom One Light.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atom One Light.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Atom One Light",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#de3e35",
|
||||||
|
"green": "#3f953a",
|
||||||
|
"yellow": "#d2b67c",
|
||||||
|
"blue": "#2f5af3",
|
||||||
|
"purple": "#950095",
|
||||||
|
"cyan": "#3f953a",
|
||||||
|
"white": "#bbbbbb",
|
||||||
|
"brightBlack": "#000000",
|
||||||
|
"brightRed": "#de3e35",
|
||||||
|
"brightGreen": "#3f953a",
|
||||||
|
"brightYellow": "#d2b67c",
|
||||||
|
"brightBlue": "#2f5af3",
|
||||||
|
"brightPurple": "#a00095",
|
||||||
|
"brightCyan": "#3f953a",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#f9f9f9",
|
||||||
|
"foreground": "#2a2c33",
|
||||||
|
"cursorColor": "#bbbbbb",
|
||||||
|
"selectionBackground": "#ededed"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atom.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Atom.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Atom",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#fd5ff1",
|
||||||
|
"green": "#87c38a",
|
||||||
|
"yellow": "#ffd7b1",
|
||||||
|
"blue": "#85befd",
|
||||||
|
"purple": "#b9b6fc",
|
||||||
|
"cyan": "#85befd",
|
||||||
|
"white": "#e0e0e0",
|
||||||
|
"brightBlack": "#4c4c4c",
|
||||||
|
"brightRed": "#fd5ff1",
|
||||||
|
"brightGreen": "#94fa36",
|
||||||
|
"brightYellow": "#f5ffa8",
|
||||||
|
"brightBlue": "#96cbfe",
|
||||||
|
"brightPurple": "#b9b6fc",
|
||||||
|
"brightCyan": "#85befd",
|
||||||
|
"brightWhite": "#e0e0e0",
|
||||||
|
"background": "#161719",
|
||||||
|
"foreground": "#c5c8c6",
|
||||||
|
"cursorColor": "#d0d0d0",
|
||||||
|
"selectionBackground": "#444444"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aura.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aura.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Aura",
|
||||||
|
"black": "#110f18",
|
||||||
|
"red": "#ff6767",
|
||||||
|
"green": "#61ffca",
|
||||||
|
"yellow": "#ffca85",
|
||||||
|
"blue": "#a277ff",
|
||||||
|
"purple": "#a277ff",
|
||||||
|
"cyan": "#61ffca",
|
||||||
|
"white": "#edecee",
|
||||||
|
"brightBlack": "#4d4d4d",
|
||||||
|
"brightRed": "#ffca85",
|
||||||
|
"brightGreen": "#a277ff",
|
||||||
|
"brightYellow": "#ffca85",
|
||||||
|
"brightBlue": "#a277ff",
|
||||||
|
"brightPurple": "#a277ff",
|
||||||
|
"brightCyan": "#61ffca",
|
||||||
|
"brightWhite": "#edecee",
|
||||||
|
"background": "#15141b",
|
||||||
|
"foreground": "#edecee",
|
||||||
|
"cursorColor": "#a277ff",
|
||||||
|
"selectionBackground": "#a277ff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aurora.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Aurora.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Aurora",
|
||||||
|
"black": "#23262e",
|
||||||
|
"red": "#f0266f",
|
||||||
|
"green": "#8fd46d",
|
||||||
|
"yellow": "#ffe66d",
|
||||||
|
"blue": "#102ee4",
|
||||||
|
"purple": "#ee5d43",
|
||||||
|
"cyan": "#03d6b8",
|
||||||
|
"white": "#c74ded",
|
||||||
|
"brightBlack": "#4f545e",
|
||||||
|
"brightRed": "#f92672",
|
||||||
|
"brightGreen": "#8fd46d",
|
||||||
|
"brightYellow": "#ffe66d",
|
||||||
|
"brightBlue": "#03d6b8",
|
||||||
|
"brightPurple": "#ee5d43",
|
||||||
|
"brightCyan": "#03d6b8",
|
||||||
|
"brightWhite": "#c74ded",
|
||||||
|
"background": "#23262e",
|
||||||
|
"foreground": "#ffca28",
|
||||||
|
"cursorColor": "#ee5d43",
|
||||||
|
"selectionBackground": "#292e38"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ayu Light.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ayu Light.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Ayu Light",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#ea6c6d",
|
||||||
|
"green": "#6cbf43",
|
||||||
|
"yellow": "#eca944",
|
||||||
|
"blue": "#3199e1",
|
||||||
|
"purple": "#9e75c7",
|
||||||
|
"cyan": "#46ba94",
|
||||||
|
"white": "#bababa",
|
||||||
|
"brightBlack": "#686868",
|
||||||
|
"brightRed": "#f07171",
|
||||||
|
"brightGreen": "#86b300",
|
||||||
|
"brightYellow": "#f2ae49",
|
||||||
|
"brightBlue": "#399ee6",
|
||||||
|
"brightPurple": "#a37acc",
|
||||||
|
"brightCyan": "#4cbf99",
|
||||||
|
"brightWhite": "#d1d1d1",
|
||||||
|
"background": "#f8f9fa",
|
||||||
|
"foreground": "#5c6166",
|
||||||
|
"cursorColor": "#ffaa33",
|
||||||
|
"selectionBackground": "#035bd6"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ayu Mirage.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ayu Mirage.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Ayu Mirage",
|
||||||
|
"black": "#171b24",
|
||||||
|
"red": "#ed8274",
|
||||||
|
"green": "#87d96c",
|
||||||
|
"yellow": "#facc6e",
|
||||||
|
"blue": "#6dcbfa",
|
||||||
|
"purple": "#dabafa",
|
||||||
|
"cyan": "#90e1c6",
|
||||||
|
"white": "#c7c7c7",
|
||||||
|
"brightBlack": "#686868",
|
||||||
|
"brightRed": "#f28779",
|
||||||
|
"brightGreen": "#d5ff80",
|
||||||
|
"brightYellow": "#ffd173",
|
||||||
|
"brightBlue": "#73d0ff",
|
||||||
|
"brightPurple": "#dfbfff",
|
||||||
|
"brightCyan": "#95e6cb",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#1f2430",
|
||||||
|
"foreground": "#cccac2",
|
||||||
|
"cursorColor": "#ffcc66",
|
||||||
|
"selectionBackground": "#409fff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ayu.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Ayu.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Ayu",
|
||||||
|
"black": "#11151c",
|
||||||
|
"red": "#ea6c73",
|
||||||
|
"green": "#7fd962",
|
||||||
|
"yellow": "#f9af4f",
|
||||||
|
"blue": "#53bdfa",
|
||||||
|
"purple": "#cda1fa",
|
||||||
|
"cyan": "#90e1c6",
|
||||||
|
"white": "#c7c7c7",
|
||||||
|
"brightBlack": "#686868",
|
||||||
|
"brightRed": "#f07178",
|
||||||
|
"brightGreen": "#aad94c",
|
||||||
|
"brightYellow": "#ffb454",
|
||||||
|
"brightBlue": "#59c2ff",
|
||||||
|
"brightPurple": "#d2a6ff",
|
||||||
|
"brightCyan": "#95e6cb",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#0b0e14",
|
||||||
|
"foreground": "#bfbdb6",
|
||||||
|
"cursorColor": "#e6b450",
|
||||||
|
"selectionBackground": "#409fff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Banana Blueberry.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Banana Blueberry.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Banana Blueberry",
|
||||||
|
"black": "#17141f",
|
||||||
|
"red": "#ff6b7f",
|
||||||
|
"green": "#00bd9c",
|
||||||
|
"yellow": "#e6c62f",
|
||||||
|
"blue": "#22e8df",
|
||||||
|
"purple": "#dc396a",
|
||||||
|
"cyan": "#56b6c2",
|
||||||
|
"white": "#f1f1f1",
|
||||||
|
"brightBlack": "#495162",
|
||||||
|
"brightRed": "#fe9ea1",
|
||||||
|
"brightGreen": "#98c379",
|
||||||
|
"brightYellow": "#f9e46b",
|
||||||
|
"brightBlue": "#91fff4",
|
||||||
|
"brightPurple": "#da70d6",
|
||||||
|
"brightCyan": "#bcf3ff",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#191323",
|
||||||
|
"foreground": "#cccccc",
|
||||||
|
"cursorColor": "#e07d13",
|
||||||
|
"selectionBackground": "#220525"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Batman.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Batman.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Batman",
|
||||||
|
"black": "#1b1d1e",
|
||||||
|
"red": "#e6dc44",
|
||||||
|
"green": "#c8be46",
|
||||||
|
"yellow": "#f4fd22",
|
||||||
|
"blue": "#737174",
|
||||||
|
"purple": "#747271",
|
||||||
|
"cyan": "#62605f",
|
||||||
|
"white": "#c6c5bf",
|
||||||
|
"brightBlack": "#505354",
|
||||||
|
"brightRed": "#fff78e",
|
||||||
|
"brightGreen": "#fff27d",
|
||||||
|
"brightYellow": "#feed6c",
|
||||||
|
"brightBlue": "#919495",
|
||||||
|
"brightPurple": "#9a9a9d",
|
||||||
|
"brightCyan": "#a3a3a6",
|
||||||
|
"brightWhite": "#dadbd6",
|
||||||
|
"background": "#1b1d1e",
|
||||||
|
"foreground": "#6f6f6f",
|
||||||
|
"cursorColor": "#fcef0c",
|
||||||
|
"selectionBackground": "#4d504c"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Belafonte Day.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Belafonte Day.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Belafonte Day",
|
||||||
|
"black": "#20111b",
|
||||||
|
"red": "#be100e",
|
||||||
|
"green": "#858162",
|
||||||
|
"yellow": "#d08b30",
|
||||||
|
"blue": "#426a79",
|
||||||
|
"purple": "#97522c",
|
||||||
|
"cyan": "#989a9c",
|
||||||
|
"white": "#968c83",
|
||||||
|
"brightBlack": "#5e5252",
|
||||||
|
"brightRed": "#be100e",
|
||||||
|
"brightGreen": "#858162",
|
||||||
|
"brightYellow": "#d08b30",
|
||||||
|
"brightBlue": "#426a79",
|
||||||
|
"brightPurple": "#97522c",
|
||||||
|
"brightCyan": "#989a9c",
|
||||||
|
"brightWhite": "#d5ccba",
|
||||||
|
"background": "#d5ccba",
|
||||||
|
"foreground": "#45373c",
|
||||||
|
"cursorColor": "#45373c",
|
||||||
|
"selectionBackground": "#968c83"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Belafonte Night.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Belafonte Night.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Belafonte Night",
|
||||||
|
"black": "#20111b",
|
||||||
|
"red": "#be100e",
|
||||||
|
"green": "#858162",
|
||||||
|
"yellow": "#eaa549",
|
||||||
|
"blue": "#426a79",
|
||||||
|
"purple": "#97522c",
|
||||||
|
"cyan": "#989a9c",
|
||||||
|
"white": "#968c83",
|
||||||
|
"brightBlack": "#5e5252",
|
||||||
|
"brightRed": "#be100e",
|
||||||
|
"brightGreen": "#858162",
|
||||||
|
"brightYellow": "#eaa549",
|
||||||
|
"brightBlue": "#426a79",
|
||||||
|
"brightPurple": "#97522c",
|
||||||
|
"brightCyan": "#989a9c",
|
||||||
|
"brightWhite": "#d5ccba",
|
||||||
|
"background": "#20111b",
|
||||||
|
"foreground": "#968c83",
|
||||||
|
"cursorColor": "#968c83",
|
||||||
|
"selectionBackground": "#45373c"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Birds Of Paradise.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Birds Of Paradise.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Birds Of Paradise",
|
||||||
|
"black": "#573d26",
|
||||||
|
"red": "#be2d26",
|
||||||
|
"green": "#6ba18a",
|
||||||
|
"yellow": "#e99d2a",
|
||||||
|
"blue": "#5a86ad",
|
||||||
|
"purple": "#ac80a6",
|
||||||
|
"cyan": "#74a6ad",
|
||||||
|
"white": "#e0dbb7",
|
||||||
|
"brightBlack": "#9b6c4a",
|
||||||
|
"brightRed": "#e84627",
|
||||||
|
"brightGreen": "#95d8ba",
|
||||||
|
"brightYellow": "#d0d150",
|
||||||
|
"brightBlue": "#b8d3ed",
|
||||||
|
"brightPurple": "#d19ecb",
|
||||||
|
"brightCyan": "#93cfd7",
|
||||||
|
"brightWhite": "#fff9d5",
|
||||||
|
"background": "#2a1f1d",
|
||||||
|
"foreground": "#e0dbb7",
|
||||||
|
"cursorColor": "#644a33",
|
||||||
|
"selectionBackground": "#563c27"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Bathory).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Bathory).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Bathory)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#fbcb97",
|
||||||
|
"yellow": "#e78a53",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#fbcb97",
|
||||||
|
"brightYellow": "#e78a53",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Burzum).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Burzum).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Burzum)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#ddeecc",
|
||||||
|
"yellow": "#99bbaa",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#ddeecc",
|
||||||
|
"brightYellow": "#99bbaa",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Dark Funeral).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Dark Funeral).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Dark Funeral)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#d0dfee",
|
||||||
|
"yellow": "#5f81a5",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#d0dfee",
|
||||||
|
"brightYellow": "#5f81a5",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Gorgoroth).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Gorgoroth).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Gorgoroth)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#9b8d7f",
|
||||||
|
"yellow": "#8c7f70",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#9b8d7f",
|
||||||
|
"brightYellow": "#8c7f70",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Immortal).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Immortal).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Immortal)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#7799bb",
|
||||||
|
"yellow": "#556677",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#7799bb",
|
||||||
|
"brightYellow": "#556677",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Khold).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Khold).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Khold)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#eceee3",
|
||||||
|
"yellow": "#974b46",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#eceee3",
|
||||||
|
"brightYellow": "#974b46",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Marduk).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Marduk).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Marduk)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#a5aaa7",
|
||||||
|
"yellow": "#626b67",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#a5aaa7",
|
||||||
|
"brightYellow": "#626b67",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Mayhem).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Mayhem).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Mayhem)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#f3ecd4",
|
||||||
|
"yellow": "#eecc6c",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#f3ecd4",
|
||||||
|
"brightYellow": "#eecc6c",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Nile).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Nile).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Nile)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#aa9988",
|
||||||
|
"yellow": "#777755",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#aa9988",
|
||||||
|
"brightYellow": "#777755",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Venom).json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal (Venom).json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal (Venom)",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#5f8787",
|
||||||
|
"green": "#f8f7f2",
|
||||||
|
"yellow": "#79241f",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#5f8787",
|
||||||
|
"brightGreen": "#f8f7f2",
|
||||||
|
"brightYellow": "#79241f",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Black Metal.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Black Metal",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#486e6f",
|
||||||
|
"green": "#dd9999",
|
||||||
|
"yellow": "#a06666",
|
||||||
|
"blue": "#888888",
|
||||||
|
"purple": "#999999",
|
||||||
|
"cyan": "#aaaaaa",
|
||||||
|
"white": "#c1c1c1",
|
||||||
|
"brightBlack": "#404040",
|
||||||
|
"brightRed": "#486e6f",
|
||||||
|
"brightGreen": "#dd9999",
|
||||||
|
"brightYellow": "#a06666",
|
||||||
|
"brightBlue": "#888888",
|
||||||
|
"brightPurple": "#999999",
|
||||||
|
"brightCyan": "#aaaaaa",
|
||||||
|
"brightWhite": "#c1c1c1",
|
||||||
|
"background": "#000000",
|
||||||
|
"foreground": "#c1c1c1",
|
||||||
|
"cursorColor": "#c1c1c1",
|
||||||
|
"selectionBackground": "#c1c1c1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blazer.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blazer.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Blazer",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#b87a7a",
|
||||||
|
"green": "#7ab87a",
|
||||||
|
"yellow": "#b8b87a",
|
||||||
|
"blue": "#7a7ab8",
|
||||||
|
"purple": "#b87ab8",
|
||||||
|
"cyan": "#7ab8b8",
|
||||||
|
"white": "#d9d9d9",
|
||||||
|
"brightBlack": "#4c4c4c",
|
||||||
|
"brightRed": "#dbbdbd",
|
||||||
|
"brightGreen": "#bddbbd",
|
||||||
|
"brightYellow": "#dbdbbd",
|
||||||
|
"brightBlue": "#bdbddb",
|
||||||
|
"brightPurple": "#dbbddb",
|
||||||
|
"brightCyan": "#bddbdb",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#0d1926",
|
||||||
|
"foreground": "#d9e6f2",
|
||||||
|
"cursorColor": "#d9e6f2",
|
||||||
|
"selectionBackground": "#c1ddff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blue Berry Pie.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blue Berry Pie.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Blue Berry Pie",
|
||||||
|
"black": "#0a4c62",
|
||||||
|
"red": "#99246e",
|
||||||
|
"green": "#5cb1b3",
|
||||||
|
"yellow": "#eab9a8",
|
||||||
|
"blue": "#90a5bd",
|
||||||
|
"purple": "#9d54a7",
|
||||||
|
"cyan": "#7e83cc",
|
||||||
|
"white": "#f0e8d6",
|
||||||
|
"brightBlack": "#463c5d",
|
||||||
|
"brightRed": "#c87272",
|
||||||
|
"brightGreen": "#0a6c7e",
|
||||||
|
"brightYellow": "#7a3188",
|
||||||
|
"brightBlue": "#5f3d63",
|
||||||
|
"brightPurple": "#bc94b7",
|
||||||
|
"brightCyan": "#5e6071",
|
||||||
|
"brightWhite": "#0a6c7e",
|
||||||
|
"background": "#1c0c28",
|
||||||
|
"foreground": "#babab9",
|
||||||
|
"cursorColor": "#fcfad6",
|
||||||
|
"selectionBackground": "#606060"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blue Dolphin.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blue Dolphin.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Blue Dolphin",
|
||||||
|
"black": "#292d3e",
|
||||||
|
"red": "#ff8288",
|
||||||
|
"green": "#b4e88d",
|
||||||
|
"yellow": "#f4d69f",
|
||||||
|
"blue": "#82aaff",
|
||||||
|
"purple": "#e9c1ff",
|
||||||
|
"cyan": "#89ebff",
|
||||||
|
"white": "#d0d0d0",
|
||||||
|
"brightBlack": "#9094a4",
|
||||||
|
"brightRed": "#ff8b92",
|
||||||
|
"brightGreen": "#ddffa7",
|
||||||
|
"brightYellow": "#ffe585",
|
||||||
|
"brightBlue": "#9cc4ff",
|
||||||
|
"brightPurple": "#ddb0f6",
|
||||||
|
"brightCyan": "#a3f7ff",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#006984",
|
||||||
|
"foreground": "#c5f2ff",
|
||||||
|
"cursorColor": "#ffcc00",
|
||||||
|
"selectionBackground": "#2baeca"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blue Matrix.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Blue Matrix.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Blue Matrix",
|
||||||
|
"black": "#101116",
|
||||||
|
"red": "#ff5680",
|
||||||
|
"green": "#00ff9c",
|
||||||
|
"yellow": "#fffc58",
|
||||||
|
"blue": "#00b0ff",
|
||||||
|
"purple": "#d57bff",
|
||||||
|
"cyan": "#76c1ff",
|
||||||
|
"white": "#c7c7c7",
|
||||||
|
"brightBlack": "#686868",
|
||||||
|
"brightRed": "#ff6e67",
|
||||||
|
"brightGreen": "#5ffa68",
|
||||||
|
"brightYellow": "#fffc67",
|
||||||
|
"brightBlue": "#6871ff",
|
||||||
|
"brightPurple": "#d682ec",
|
||||||
|
"brightCyan": "#60fdff",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#101116",
|
||||||
|
"foreground": "#00a2ff",
|
||||||
|
"cursorColor": "#76ff9f",
|
||||||
|
"selectionBackground": "#c1deff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Bluloco Dark.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Bluloco Dark.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Bluloco Dark",
|
||||||
|
"black": "#41444d",
|
||||||
|
"red": "#fc2f52",
|
||||||
|
"green": "#25a45c",
|
||||||
|
"yellow": "#ff936a",
|
||||||
|
"blue": "#3476ff",
|
||||||
|
"purple": "#7a82da",
|
||||||
|
"cyan": "#4483aa",
|
||||||
|
"white": "#cdd4e0",
|
||||||
|
"brightBlack": "#8f9aae",
|
||||||
|
"brightRed": "#ff6480",
|
||||||
|
"brightGreen": "#3fc56b",
|
||||||
|
"brightYellow": "#f9c859",
|
||||||
|
"brightBlue": "#10b1fe",
|
||||||
|
"brightPurple": "#ff78f8",
|
||||||
|
"brightCyan": "#5fb9bc",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#282c34",
|
||||||
|
"foreground": "#b9c0cb",
|
||||||
|
"cursorColor": "#ffcc00",
|
||||||
|
"selectionBackground": "#b9c0ca"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Bluloco Light.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Bluloco Light.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Bluloco Light",
|
||||||
|
"black": "#373a41",
|
||||||
|
"red": "#d52753",
|
||||||
|
"green": "#23974a",
|
||||||
|
"yellow": "#df631c",
|
||||||
|
"blue": "#275fe4",
|
||||||
|
"purple": "#823ff1",
|
||||||
|
"cyan": "#27618d",
|
||||||
|
"white": "#babbc2",
|
||||||
|
"brightBlack": "#676a77",
|
||||||
|
"brightRed": "#ff6480",
|
||||||
|
"brightGreen": "#3cbc66",
|
||||||
|
"brightYellow": "#c5a332",
|
||||||
|
"brightBlue": "#0099e1",
|
||||||
|
"brightPurple": "#ce33c0",
|
||||||
|
"brightCyan": "#6d93bb",
|
||||||
|
"brightWhite": "#d3d3d3",
|
||||||
|
"background": "#f9f9f9",
|
||||||
|
"foreground": "#373a41",
|
||||||
|
"cursorColor": "#f32759",
|
||||||
|
"selectionBackground": "#daf0ff"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Borland.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Borland.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Borland",
|
||||||
|
"black": "#4f4f4f",
|
||||||
|
"red": "#ff6c60",
|
||||||
|
"green": "#a8ff60",
|
||||||
|
"yellow": "#ffffb6",
|
||||||
|
"blue": "#96cbfe",
|
||||||
|
"purple": "#ff73fd",
|
||||||
|
"cyan": "#c6c5fe",
|
||||||
|
"white": "#eeeeee",
|
||||||
|
"brightBlack": "#7c7c7c",
|
||||||
|
"brightRed": "#ffb6b0",
|
||||||
|
"brightGreen": "#ceffac",
|
||||||
|
"brightYellow": "#ffffcc",
|
||||||
|
"brightBlue": "#b5dcff",
|
||||||
|
"brightPurple": "#ff9cfe",
|
||||||
|
"brightCyan": "#dfdffe",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#0000a4",
|
||||||
|
"foreground": "#ffff4e",
|
||||||
|
"cursorColor": "#ffa560",
|
||||||
|
"selectionBackground": "#a4a4a4"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Box.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Box.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Box",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#cc0403",
|
||||||
|
"green": "#19cb00",
|
||||||
|
"yellow": "#cecb00",
|
||||||
|
"blue": "#0d73cc",
|
||||||
|
"purple": "#cb1ed1",
|
||||||
|
"cyan": "#0dcdcd",
|
||||||
|
"white": "#dddddd",
|
||||||
|
"brightBlack": "#767676",
|
||||||
|
"brightRed": "#f2201f",
|
||||||
|
"brightGreen": "#23fd00",
|
||||||
|
"brightYellow": "#fffd00",
|
||||||
|
"brightBlue": "#1a8fff",
|
||||||
|
"brightPurple": "#fd28ff",
|
||||||
|
"brightCyan": "#14ffff",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#141d2b",
|
||||||
|
"foreground": "#9fef00",
|
||||||
|
"cursorColor": "#9fef00",
|
||||||
|
"selectionBackground": "#a4b1cd"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Breadog.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Breadog.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Breadog",
|
||||||
|
"black": "#362c24",
|
||||||
|
"red": "#b10b00",
|
||||||
|
"green": "#007232",
|
||||||
|
"yellow": "#8b4c00",
|
||||||
|
"blue": "#005cb4",
|
||||||
|
"purple": "#9b0097",
|
||||||
|
"cyan": "#006a78",
|
||||||
|
"white": "#baa99d",
|
||||||
|
"brightBlack": "#514337",
|
||||||
|
"brightRed": "#de1100",
|
||||||
|
"brightGreen": "#008f40",
|
||||||
|
"brightYellow": "#ae6000",
|
||||||
|
"brightBlue": "#0074e1",
|
||||||
|
"brightPurple": "#c300bd",
|
||||||
|
"brightCyan": "#008697",
|
||||||
|
"brightWhite": "#eae1da",
|
||||||
|
"background": "#f1ebe6",
|
||||||
|
"foreground": "#362c24",
|
||||||
|
"cursorColor": "#362c24",
|
||||||
|
"selectionBackground": "#362c24"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Breeze.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Breeze.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Breeze",
|
||||||
|
"black": "#31363b",
|
||||||
|
"red": "#ed1515",
|
||||||
|
"green": "#11d116",
|
||||||
|
"yellow": "#f67400",
|
||||||
|
"blue": "#1d99f3",
|
||||||
|
"purple": "#9b59b6",
|
||||||
|
"cyan": "#1abc9c",
|
||||||
|
"white": "#eff0f1",
|
||||||
|
"brightBlack": "#7f8c8d",
|
||||||
|
"brightRed": "#c0392b",
|
||||||
|
"brightGreen": "#1cdc9a",
|
||||||
|
"brightYellow": "#fdbc4b",
|
||||||
|
"brightBlue": "#3daee9",
|
||||||
|
"brightPurple": "#8e44ad",
|
||||||
|
"brightCyan": "#16a085",
|
||||||
|
"brightWhite": "#fcfcfc",
|
||||||
|
"background": "#31363b",
|
||||||
|
"foreground": "#eff0f1",
|
||||||
|
"cursorColor": "#eff0f1",
|
||||||
|
"selectionBackground": "#eff0f1"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Bright Lights.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Bright Lights.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Bright Lights",
|
||||||
|
"black": "#191919",
|
||||||
|
"red": "#ff355b",
|
||||||
|
"green": "#b7e876",
|
||||||
|
"yellow": "#ffc251",
|
||||||
|
"blue": "#76d4ff",
|
||||||
|
"purple": "#ba76e7",
|
||||||
|
"cyan": "#6cbfb5",
|
||||||
|
"white": "#c2c8d7",
|
||||||
|
"brightBlack": "#4c4c4c",
|
||||||
|
"brightRed": "#ff355b",
|
||||||
|
"brightGreen": "#b7e876",
|
||||||
|
"brightYellow": "#ffc251",
|
||||||
|
"brightBlue": "#76d5ff",
|
||||||
|
"brightPurple": "#ba76e7",
|
||||||
|
"brightCyan": "#6cbfb5",
|
||||||
|
"brightWhite": "#c2c8d7",
|
||||||
|
"background": "#191919",
|
||||||
|
"foreground": "#b3c9d7",
|
||||||
|
"cursorColor": "#f34b00",
|
||||||
|
"selectionBackground": "#b3c9d7"
|
||||||
|
}
|
||||||
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Broadcast.json
vendored
Normal file
23
third_party/KodoTerm/KodoTermThemes/windowsterminal/Broadcast.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"black": "#000000",
|
||||||
|
"red": "#da4939",
|
||||||
|
"green": "#519f50",
|
||||||
|
"yellow": "#ffd24a",
|
||||||
|
"blue": "#6d9cbe",
|
||||||
|
"purple": "#d0d0ff",
|
||||||
|
"cyan": "#6e9cbe",
|
||||||
|
"white": "#ffffff",
|
||||||
|
"brightBlack": "#585858",
|
||||||
|
"brightRed": "#ff7b6b",
|
||||||
|
"brightGreen": "#83d182",
|
||||||
|
"brightYellow": "#ffff7c",
|
||||||
|
"brightBlue": "#9fcef0",
|
||||||
|
"brightPurple": "#ffffff",
|
||||||
|
"brightCyan": "#a0cef0",
|
||||||
|
"brightWhite": "#ffffff",
|
||||||
|
"background": "#2b2b2b",
|
||||||
|
"foreground": "#e6e1dc",
|
||||||
|
"cursorColor": "#ffffff",
|
||||||
|
"selectionBackground": "#5a647e"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user