Added installer
This commit is contained in:
commit
c337e35697
|
@ -0,0 +1,111 @@
|
|||
#!/bin/bash
|
||||
|
||||
BLUE='\033[0;34m'.
|
||||
NO_COLOR='\033[0m'
|
||||
|
||||
if (( $EUID != 0 )); then
|
||||
echo "Please run as root"
|
||||
exit
|
||||
fi
|
||||
|
||||
clear
|
||||
|
||||
installTheme(){
|
||||
cd /var/www/
|
||||
tar -cvf IceMinecraftTheme.tar.gz pterodactyl
|
||||
echo "Installing theme..."
|
||||
cd /var/www/pterodactyl
|
||||
rm -r IceMinecraftTheme
|
||||
git clone https://github.com/Angelillo15/IceMinecraftTheme.git
|
||||
cd MinecraftPurpleTheme
|
||||
rm /var/www/pterodactyl/resources/scripts/IceMinecraftTheme.css
|
||||
rm /var/www/pterodactyl/resources/scripts/index.tsx
|
||||
rm /var/www/pterodactyl/resources/scripts/components/server/console/Console.tsx
|
||||
mv index.tsx /var/www/pterodactyl/resources/scripts/index.tsx
|
||||
mv IceMinecraftTheme.css /var/www/pterodactyl/resources/scripts/IceMinecraftTheme.css
|
||||
mv components/server/console/Console.tsx /var/www/pterodactyl/resources/scripts/components/server/console/Console.tsx
|
||||
cd /var/www/pterodactyl
|
||||
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
|
||||
apt update
|
||||
apt install -y nodejs
|
||||
|
||||
npm i -g yarn
|
||||
yarn
|
||||
|
||||
cd /var/www/pterodactyl
|
||||
yarn build:production
|
||||
sudo php artisan optimize:clear
|
||||
|
||||
|
||||
}
|
||||
|
||||
installThemeQuestion(){
|
||||
while true; do
|
||||
read -p "Are you sure that you want to install the theme [y/N]? " yn
|
||||
case $yn in
|
||||
[Yy]* ) installTheme; break;;
|
||||
[Nn]* ) exit;;
|
||||
* ) exit;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
repair(){
|
||||
bash <(curl https://raw.githubusercontent.com/Angelillo15/IceMinecraftTheme/main/repair.sh)
|
||||
}
|
||||
|
||||
restoreBackUp(){
|
||||
echo "Restoring backup..."
|
||||
cd /var/www/
|
||||
tar -xvf IceMinecraftTheme.tar.gz
|
||||
rm IceMinecraftTheme.tar.gz
|
||||
|
||||
cd /var/www/pterodactyl
|
||||
yarn build:production
|
||||
sudo php artisan optimize:clear
|
||||
}
|
||||
|
||||
printf "${blue} ____ _____ ______ _________________ ____ ____ ______ ______ _______ ______ \n"
|
||||
printf "${blue}| | ___|\ \ ___|\ \ / \| | | | ___|\ \ | \/ \ ___|\ \ \n"
|
||||
printf "${blue}| | / /\ \ | \ \ \______ ______/| | | || \ \ / /\ \ | \ \ \n"
|
||||
printf "${blue}| || | | || ,_____/| \( / / )/ | |_| || ,_____/| / /\ / /\ || ,_____/| \n"
|
||||
printf "${blue}| || | |____|| \--'\_|/ ' | | ' | .-. || \--'\_|/ / /\ \_/ / / /|| \--'\_|/ \n"
|
||||
printf "${blue}| || | ____ | /___/| | | | | | || /___/| | | \|_|/ / / || /___/| \n"
|
||||
printf "${blue}| || | | || \____|\ / // | | | || \____|\ | | | | || \____|\ \n"
|
||||
printf "${blue}|____||\ ___\/ /||____ ' /| /___// |____| |____||____ ' /||\____\ |____| /|____ ' /| \n"
|
||||
printf "${blue}| || | /____/ || /_____/ | | | | | | || /_____/ || | | | | / | /_____/ | \n"
|
||||
printf "${blue}|____| \|___| | /|____| | / |____| |____| |____||____| | / \|____| |____|/ |____| | / \n"
|
||||
printf "${blue} \( \( |____|/ \( |_____|/ \( \( )/ \( |_____|/ \( )/ \( |_____|/ \n"
|
||||
printf "${blue} ' ' )/ ' )/ ' ' ' ' )/ ' ' ' )/ \n"
|
||||
printf "${blue} ' ' ' ' \n"
|
||||
echo ""
|
||||
echo "Copyright (c) 2022 Angelillo15 | angelillo15.es"
|
||||
echo "This program is free software: you can redistribute it and/or modify"
|
||||
echo ""
|
||||
echo "Discord: https://discord.angelillo15.es/"
|
||||
echo "Website: https://angelillo15.es/"
|
||||
echo ""
|
||||
echo "[1] Install theme"
|
||||
echo "[2] Restore backup"
|
||||
echo "[3] Repair panel (use if you have an error in the theme installation)"
|
||||
echo "[4] Exit"
|
||||
printf "${NO_COLOR}"
|
||||
|
||||
read -p "Please enter a number: " choice
|
||||
if [ $choice == "1" ]
|
||||
then
|
||||
installThemeQuestion
|
||||
fi
|
||||
if [ $choice == "2" ]
|
||||
then
|
||||
restoreBackUp
|
||||
fi
|
||||
if [ $choice == "3" ]
|
||||
then
|
||||
repair
|
||||
fi
|
||||
if [ $choice == "4" ]
|
||||
then
|
||||
exit
|
||||
fi
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/bash
|
||||
|
||||
if (( $EUID != 0 )); then
|
||||
echo "Please run as root"
|
||||
exit
|
||||
fi
|
||||
|
||||
repairPanel(){
|
||||
cd /var/www/pterodactyl
|
||||
|
||||
php artisan down
|
||||
|
||||
rm -r /var/www/pterodactyl/resources
|
||||
|
||||
curl -L https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz | tar -xzv
|
||||
|
||||
chmod -R 755 storage/* bootstrap/cache
|
||||
|
||||
composer install --no-dev --optimize-autoloader
|
||||
|
||||
php artisan view:clear
|
||||
|
||||
php artisan config:clear
|
||||
|
||||
php artisan migrate --seed --force
|
||||
|
||||
chown -R www-data:www-data /var/www/pterodactyl/*
|
||||
|
||||
php artisan queue:restart
|
||||
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
|
||||
|
||||
apt update
|
||||
|
||||
apt install -y nodejs
|
||||
|
||||
npm i -g yarn
|
||||
|
||||
yarn
|
||||
|
||||
yarn build:production
|
||||
|
||||
sudo php artisan optimize:clear
|
||||
|
||||
php artisan up
|
||||
}
|
||||
|
||||
while true; do
|
||||
read -p "Are you sure that you want to repair the panel [y/N]? " yn
|
||||
case $yn in
|
||||
[Yy]* ) repairPanel; break;;
|
||||
[Nn]* ) exit;;
|
||||
* ) exit;;
|
||||
esac
|
||||
done
|
|
@ -0,0 +1,268 @@
|
|||
:root {
|
||||
--main-color: #0D181E;
|
||||
--secundary-color: #132537;
|
||||
--tertiary-color: #0a354d;
|
||||
--bacground-url: url("https://i.imgur.com/u6HYFAY.png");
|
||||
}
|
||||
|
||||
html {
|
||||
background-image: var(--bacground-url);
|
||||
background-attachment: fixed;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
a.GreyRowBox-sc-1xo9c6v-0.ServerRow__StatusIndicatorBox-sc-1ibsw91-2.dyLna-D.fRwFrz.DashboardContainer___StyledServerRow-sc-1topkxf-2 {
|
||||
background-color: var(--main-color) !important;
|
||||
}
|
||||
|
||||
a.GreyRowBox-sc-1xo9c6v-0.ServerRow__StatusIndicatorBox-sc-1ibsw91-2.dyLna-D.fwbDSe.DashboardContainer___StyledServerRow-sc-1topkxf-2.jbVWLN {
|
||||
background-color: var(--main-color) !important;
|
||||
background-size: cover;
|
||||
|
||||
}
|
||||
|
||||
div.SubNavigation-sc-lfuaoi-0 {
|
||||
background-color: rgba(255, 255, 255, 0) !important;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1) !important;
|
||||
|
||||
}
|
||||
|
||||
div.grid.grid-cols-6.gap-2.md:gap-4.col-span-4.lg:col-span-1.order-last.lg:order-none {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
div.style-module_2Vp6MaXq.bg-gray-600.cursor-pointer {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(9.2px);
|
||||
-webkit-backdrop-filter: blur(9.2px);
|
||||
}
|
||||
|
||||
div.style-module_2Vp6MaXq.bg-gray-600 {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(9.2px);
|
||||
-webkit-backdrop-filter: blur(9.2px);
|
||||
}
|
||||
|
||||
div.style-module_2XbmHEcn.group {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.w-full.bg-neutral-900.shadow-md.overflow-x-auto {
|
||||
background-color: var(--main-color)
|
||||
}
|
||||
|
||||
div.icon.mr-4 {
|
||||
background-color: var(--secundary-color)
|
||||
}
|
||||
|
||||
a.style-module_35MPv1CD.style-module_35MPv1CD.active {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
|
||||
.style-module_1WqkLT9X {
|
||||
background-color: var(--main-color) !important;
|
||||
}
|
||||
|
||||
.style-module_1WqkLT9X:hover {
|
||||
background-color: var(--secundary-color) !important;
|
||||
}
|
||||
|
||||
.style-module_35MPv1CD.active {
|
||||
background-color: var(--main-color) !important;
|
||||
}
|
||||
|
||||
.style-module_35MPv1CD.active:hover {
|
||||
background-color: var(--secundary-color) !important;
|
||||
}
|
||||
|
||||
div.GreyRowBox-sc-1xo9c6v-0.iTERJN.flex-wrap.md:flex-nowrap.mt-2 {
|
||||
background-color: var(--secundary-color)
|
||||
}
|
||||
|
||||
div.TitledGreyBox___StyledDiv3-sc-gvsoy-4.fKIIIQ {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
input.Input-sc-19rce1w-0.jqTCDz.cursor-pointer {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
input.Input-sc-19rce1w-0.jqTCDz {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.grid.grid-cols-10.py-4.border-b-2.border-gray-800.last:rounded-b.last:border-0.group {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.TitledGreyBox___StyledDiv2-sc-gvsoy-1.jRrWLs {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
|
||||
div.ContentBox___StyledDiv-sc-mjlt6f-2.iGOcRf {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.Modal___StyledDiv2-sc-9vzni8-3.ekHIsr {
|
||||
background-color: var(--secundary-color)
|
||||
}
|
||||
|
||||
|
||||
div.GreyRowBox-sc-1xo9c6v-0.DatabaseRow___StyledGreyRowBox-sc-1t67zwr-13.iTERJN.gkSIus {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.EditScheduleModal___StyledDiv2-sc-wh9db9-4.fhEpAC {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.EditScheduleModal___StyledDiv4-sc-wh9db9-6.jyDDEO {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.EditScheduleModal___StyledDiv5-sc-wh9db9-7.ueIjC {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
a.GreyRowBox-sc-1xo9c6v-0.ScheduleContainer___StyledGreyRowBox-sc-dlqnx9-2.dyLna-D.bppajE {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.GreyRowBox-sc-1xo9c6v-0.UserRow___StyledGreyRowBox-sc-hg2wjz-0.dyLna-D.dmlaEn {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.CreateBackupButton___StyledDiv2-sc-da8bqw-3.eDncUf {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
textarea.Input__Textarea-sc-19rce1w-1.kKFWRA {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.flex.justify-end.space-x-4.mt-4.w-full.md:mt-0.md:w-48 {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
select.Select-sc-17exaqp-0.dupyoa {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
div.TitledGreyBox___StyledDiv-sc-gvsoy-0.oLbNP.StartupContainer___StyledTitledGreyBox-sc-163imy2-1.kRunTE {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
a.GreyRowBox-sc-1xo9c6v-0.ServerRow__StatusIndicatorBox-sc-1ibsw91-2.dyLna-D {
|
||||
background-color: var(--main-color)
|
||||
}
|
||||
|
||||
label.PermissionRow__Container-sc-1h899cn-0.icxFlO:hover {
|
||||
background-color: var(--tertiary-color);
|
||||
}
|
||||
|
||||
div.style-module_1DtraXMW.bg-gray-700 {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
input.Input-sc-19rce1w-0.jqTCDz {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
div.TitledGreyBox___StyledDiv-sc-gvsoy-0.oLbNP {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
div.grid.grid-cols-10.py-4.border-b-2.border-gray-800.last:rounded-b.last:border-0.group {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
.group {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
.GreyRowBox-sc-1xo9c6v-0 {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
#app {
|
||||
background-image: var(--bacground-url);
|
||||
background-attachment: fixed;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/*
|
||||
Server Console style module
|
||||
*/
|
||||
|
||||
div.server-console-style-module__stat_block {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(9.2px);
|
||||
-webkit-backdrop-filter: blur(9.2px);
|
||||
}
|
||||
|
||||
div.server-console-style-module__icon.bg-gray-700 {
|
||||
background-color: var(--secundary-color);
|
||||
}
|
||||
|
||||
button.style-module_4LBM1DKx {
|
||||
/* From https://css.glass */
|
||||
background: rgba(255, 255, 255, 0);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(8.6px);
|
||||
-webkit-backdrop-filter: blur(8.6px);
|
||||
}
|
||||
|
||||
div.server-console-style-module__chart_container {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(9.2px);
|
||||
-webkit-backdrop-filter: blur(9.2px);
|
||||
}
|
||||
|
||||
button.style-module_4LBM1DKx.style-module_3kBDV_wo.style-module_Yp7-2Fw-.flex-1:hover {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
input.Input-sc-19rce1w-0.ZkNLd {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
input.Input-sc-19rce1w-0.jqTCDz {
|
||||
background-color: var(--secundary-color);
|
||||
;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: overlay;
|
||||
}
|
||||
|
||||
Input {
|
||||
background-color: var(--secundary-color) !important;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: var(--secundary-color) !important;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: var(--secundary-color) !important;
|
||||
}
|
||||
|
||||
p.StartupContainer___StyledP-sc-163imy2-3.ekbHAG {
|
||||
background-color: var(--secundary-color) !important;
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ITerminalOptions, Terminal } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { SearchAddon } from 'xterm-addon-search';
|
||||
import { SearchBarAddon } from 'xterm-addon-search-bar';
|
||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
import { ScrollDownHelperAddon } from '@/plugins/XtermScrollDownHelperAddon';
|
||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||
import { ServerContext } from '@/state/server';
|
||||
import { usePermissions } from '@/plugins/usePermissions';
|
||||
import { theme as th } from 'twin.macro';
|
||||
import useEventListener from '@/plugins/useEventListener';
|
||||
import { debounce } from 'debounce';
|
||||
import { usePersistedState } from '@/plugins/usePersistedState';
|
||||
import { SocketEvent, SocketRequest } from '@/components/server/events';
|
||||
import classNames from 'classnames';
|
||||
import { ChevronDoubleRightIcon } from '@heroicons/react/solid';
|
||||
|
||||
import 'xterm/css/xterm.css';
|
||||
import styles from './style.module.css';
|
||||
|
||||
const theme = {
|
||||
background: '#111F26',
|
||||
cursor: 'transparent',
|
||||
black: th`colors.black`.toString(),
|
||||
red: '#E54B4B',
|
||||
green: '#9ECE58',
|
||||
yellow: '#FAED70',
|
||||
blue: '#396FE2',
|
||||
magenta: '#BB80B3',
|
||||
cyan: '#2DDAFD',
|
||||
white: '#d0d0d0',
|
||||
brightBlack: 'rgba(255, 255, 255, 0.2)',
|
||||
brightRed: '#FF5370',
|
||||
brightGreen: '#C3E88D',
|
||||
brightYellow: '#FFCB6B',
|
||||
brightBlue: '#82AAFF',
|
||||
brightMagenta: '#C792EA',
|
||||
brightCyan: '#89DDFF',
|
||||
brightWhite: '#ffffff',
|
||||
selection: '#FAF089',
|
||||
};
|
||||
|
||||
const terminalProps: ITerminalOptions = {
|
||||
disableStdin: true,
|
||||
cursorStyle: 'underline',
|
||||
allowTransparency: true,
|
||||
fontSize: 12,
|
||||
fontFamily: th('fontFamily.mono'),
|
||||
rows: 30,
|
||||
theme: theme,
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const TERMINAL_PRELUDE = '\u001b[1m\u001b[33mcontainer@pterodactyl~ \u001b[0m';
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const terminal = useMemo(() => new Terminal({ ...terminalProps }), []);
|
||||
const fitAddon = new FitAddon();
|
||||
const searchAddon = new SearchAddon();
|
||||
const searchBar = new SearchBarAddon({ searchAddon });
|
||||
const webLinksAddon = new WebLinksAddon();
|
||||
const scrollDownHelperAddon = new ScrollDownHelperAddon();
|
||||
const { connected, instance } = ServerContext.useStoreState((state) => state.socket);
|
||||
const [canSendCommands] = usePermissions(['control.console']);
|
||||
const serverId = ServerContext.useStoreState((state) => state.server.data!.id);
|
||||
const isTransferring = ServerContext.useStoreState((state) => state.server.data!.isTransferring);
|
||||
const [history, setHistory] = usePersistedState<string[]>(`${serverId}:command_history`, []);
|
||||
const [historyIndex, setHistoryIndex] = useState(-1);
|
||||
|
||||
const handleConsoleOutput = (line: string, prelude = false) =>
|
||||
terminal.writeln((prelude ? TERMINAL_PRELUDE : '') + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m');
|
||||
|
||||
const handleTransferStatus = (status: string) => {
|
||||
switch (status) {
|
||||
// Sent by either the source or target node if a failure occurs.
|
||||
case 'failure':
|
||||
terminal.writeln(TERMINAL_PRELUDE + 'Transfer has failed.\u001b[0m');
|
||||
return;
|
||||
|
||||
// Sent by the source node whenever the server was archived successfully.
|
||||
case 'archive':
|
||||
terminal.writeln(
|
||||
TERMINAL_PRELUDE + 'Server has been archived successfully, attempting connection to target node..\u001b[0m'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDaemonErrorOutput = (line: string) =>
|
||||
terminal.writeln(TERMINAL_PRELUDE + '\u001b[1m\u001b[41m' + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m');
|
||||
|
||||
const handlePowerChangeEvent = (state: string) =>
|
||||
terminal.writeln(TERMINAL_PRELUDE + 'Server marked as ' + state + '...\u001b[0m');
|
||||
|
||||
const handleCommandKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'ArrowUp') {
|
||||
const newIndex = Math.min(historyIndex + 1, history!.length - 1);
|
||||
|
||||
setHistoryIndex(newIndex);
|
||||
e.currentTarget.value = history![newIndex] || '';
|
||||
|
||||
// By default up arrow will also bring the cursor to the start of the line,
|
||||
// so we'll preventDefault to keep it at the end.
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
const newIndex = Math.max(historyIndex - 1, -1);
|
||||
|
||||
setHistoryIndex(newIndex);
|
||||
e.currentTarget.value = history![newIndex] || '';
|
||||
}
|
||||
|
||||
const command = e.currentTarget.value;
|
||||
if (e.key === 'Enter' && command.length > 0) {
|
||||
setHistory((prevHistory) => [command, ...prevHistory!].slice(0, 32));
|
||||
setHistoryIndex(-1);
|
||||
|
||||
instance && instance.send('send command', command);
|
||||
e.currentTarget.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (connected && ref.current && !terminal.element) {
|
||||
terminal.loadAddon(fitAddon);
|
||||
terminal.loadAddon(searchAddon);
|
||||
terminal.loadAddon(searchBar);
|
||||
terminal.loadAddon(webLinksAddon);
|
||||
terminal.loadAddon(scrollDownHelperAddon);
|
||||
|
||||
terminal.open(ref.current);
|
||||
fitAddon.fit();
|
||||
|
||||
// Add support for capturing keys
|
||||
terminal.attachCustomKeyEventHandler((e: KeyboardEvent) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
|
||||
document.execCommand('copy');
|
||||
return false;
|
||||
} else if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
||||
e.preventDefault();
|
||||
searchBar.show();
|
||||
return false;
|
||||
} else if (e.key === 'Escape') {
|
||||
searchBar.hidden();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}, [terminal, connected]);
|
||||
|
||||
useEventListener(
|
||||
'resize',
|
||||
debounce(() => {
|
||||
if (terminal.element) {
|
||||
fitAddon.fit();
|
||||
}
|
||||
}, 100)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const listeners: Record<string, (s: string) => void> = {
|
||||
[SocketEvent.STATUS]: handlePowerChangeEvent,
|
||||
[SocketEvent.CONSOLE_OUTPUT]: handleConsoleOutput,
|
||||
[SocketEvent.INSTALL_OUTPUT]: handleConsoleOutput,
|
||||
[SocketEvent.TRANSFER_LOGS]: handleConsoleOutput,
|
||||
[SocketEvent.TRANSFER_STATUS]: handleTransferStatus,
|
||||
[SocketEvent.DAEMON_MESSAGE]: (line) => handleConsoleOutput(line, true),
|
||||
[SocketEvent.DAEMON_ERROR]: handleDaemonErrorOutput,
|
||||
};
|
||||
|
||||
if (connected && instance) {
|
||||
// Do not clear the console if the server is being transferred.
|
||||
if (!isTransferring) {
|
||||
terminal.clear();
|
||||
}
|
||||
|
||||
Object.keys(listeners).forEach((key: string) => {
|
||||
instance.addListener(key, listeners[key]);
|
||||
});
|
||||
instance.send(SocketRequest.SEND_LOGS);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (instance) {
|
||||
Object.keys(listeners).forEach((key: string) => {
|
||||
instance.removeListener(key, listeners[key]);
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [connected, instance]);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.terminal, 'relative')}>
|
||||
<SpinnerOverlay visible={!connected} size={'large'} />
|
||||
<div className={classNames(styles.container, styles.overflows_container, { 'rounded-b': !canSendCommands })}>
|
||||
<div className={'h-full'}>
|
||||
<div id={styles.terminal} ref={ref} />
|
||||
</div>
|
||||
</div>
|
||||
{canSendCommands && (
|
||||
<div className={classNames('relative', styles.overflows_container)}>
|
||||
<input
|
||||
className={classNames('peer', styles.command_input)}
|
||||
type={'text'}
|
||||
placeholder={'Type a command...'}
|
||||
aria-label={'Console command input.'}
|
||||
disabled={!instance || !connected}
|
||||
onKeyDown={handleCommandKeyDown}
|
||||
autoCorrect={'off'}
|
||||
autoCapitalize={'none'}
|
||||
/>
|
||||
<div
|
||||
className={classNames(
|
||||
'text-gray-100 peer-focus:text-gray-50 peer-focus:animate-pulse',
|
||||
styles.command_icon
|
||||
)}
|
||||
>
|
||||
<ChevronDoubleRightIcon className={'w-4 h-4'} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from '@/components/App';
|
||||
import { setConfig } from 'react-hot-loader';
|
||||
import './IceMinecraftTheme.css';
|
||||
|
||||
// Enable language support.
|
||||
import './i18n';
|
||||
|
||||
// Prevents page reloads while making component changes which
|
||||
// also avoids triggering constant loading indicators all over
|
||||
// the place in development.
|
||||
//
|
||||
// @see https://github.com/gaearon/react-hot-loader#hook-support
|
||||
setConfig({ reloadHooks: false });
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('app'));
|
Loading…
Reference in New Issue