first commit

This commit is contained in:
MCHost
2025-06-16 10:11:55 -04:00
commit 612ae12863
17 changed files with 4324 additions and 0 deletions

1405
public/app.js Normal file

File diff suppressed because it is too large Load Diff

222
public/css/styles.css Normal file
View File

@ -0,0 +1,222 @@
@import "tailwindcss";
@layer base {
/* Sticky footer base styles */
html {
height: 100%;
}
body {
min-height: 100%;
display: flex;
flex-direction: column;
}
#app {
flex: 1 0 auto; /* Make #app grow to fill available space */
display: flex;
flex-direction: column;
}
main {
flex-grow: 1; /* Ensure main grows within #app */
}
footer {
flex-shrink: 0; /* Prevent footer from shrinking */
}
}
@layer components {
.spinner {
border: 4px solid rgba(255, 225, 225, 0.3);
border-top: 4px solid #ffffff;
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 1s linear infinite;
display: inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#notificationContainer {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}
.notification {
background-color: #1f2937;
color: white;
padding: 16px;
border-radius: 8px;
margin-top: 8px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: opacity 0.3s ease;
}
.notification.success {
background-color: #158106;
}
.notification.error {
background-color: #b91c1c;
}
#dockerLogsTerminal {
background-color: #1f2937;
padding: 1rem;
border-radius: 0.5rem;
max-height: 12rem;
width: 100%;
}
.xterm .xterm-viewport {
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #4b5563 #1f2937;
}
.xterm .xterm-viewport::-webkit-scrollbar {
width: 8px;
}
.xterm .xterm-viewport::-webkit-scrollbar-track {
background: #1f2937;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
.xterm .xterm-screen {
width: 100% !important;
}
.control-btn {
padding: 0.5rem 1rem;
font-size: 0.875rem;
line-height: 1.25rem;
transition: all 0.2s ease;
min-width: 80px;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.control-btn:hover:not(.disabled-btn) {
transform: translateY(-1px);
}
.control-btn:active:not(.disabled-btn) {
transform: translateY(0);
}
.modal {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.75);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: #1f2937;
padding: 1.5rem;
border-radius: 0.5rem;
width: 100%;
max-width: 600px;
position: relative;
max-height: 80vh;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #4b5563 #1f2937;
}
.modal-content::-webkit-scrollbar {
width: 8px;
}
.modal-content::-webkit-scrollbar-track {
background: #1f2937;
}
.modal-content::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
.modal-content::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
.modal-close {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: none;
border: none;
color: white;
font-size: 1.25rem;
cursor: pointer;
}
.disabled-btn {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
}
@layer utilities {
/* Add any custom utilities if needed */
}
/* Media queries */
@media (max-width: 640px) {
.bg-gray-800.p-6.rounded-lg.shadow-lg .grid {
grid-template-columns: 1fr;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg p {
display: flex;
flex-direction: column;
gap: 0.5rem;
word-break: break-all;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg a,
.bg-gray-800.p-6.rounded-lg.shadow-lg span {
word-break: break-all;
overflow-wrap: anywhere;
white-space: normal;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg button {
width: 100%;
margin-top: 0.5rem;
}
}
/* Additional styles */
.bg-gray-800.p-6.rounded-lg.shadow-lg .grid {
overflow-x: hidden;
}
.bg-gray-800.p-6.rounded-lg.shadow p {
max-width: 100%;
}

871
public/css/styles.min.css vendored Normal file
View File

@ -0,0 +1,871 @@
/*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
:root, :host {
--font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--color-red-500: oklch(63.7% 0.237 25.331);
--color-red-600: oklch(57.7% 0.245 27.325);
--color-red-700: oklch(50.5% 0.213 27.518);
--color-yellow-600: oklch(68.1% 0.162 75.834);
--color-yellow-700: oklch(55.4% 0.135 66.442);
--color-green-600: oklch(62.7% 0.194 149.214);
--color-green-700: oklch(52.7% 0.154 150.069);
--color-blue-400: oklch(70.7% 0.165 254.624);
--color-blue-500: oklch(62.3% 0.214 259.815);
--color-blue-600: oklch(54.6% 0.245 262.881);
--color-blue-700: oklch(48.8% 0.243 264.376);
--color-purple-600: oklch(55.8% 0.288 302.321);
--color-purple-700: oklch(49.6% 0.265 301.924);
--color-gray-400: oklch(70.7% 0.022 261.325);
--color-gray-600: oklch(44.6% 0.03 256.802);
--color-gray-700: oklch(37.3% 0.034 259.733);
--color-gray-800: oklch(27.8% 0.033 256.848);
--color-gray-900: oklch(21% 0.034 264.665);
--color-white: #fff;
--spacing: 0.25rem;
--container-md: 28rem;
--text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875);
--text-lg: 1.125rem;
--text-lg--line-height: calc(1.75 / 1.125);
--text-xl: 1.25rem;
--text-xl--line-height: calc(1.75 / 1.25);
--text-2xl: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5);
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--radius-lg: 0.5rem;
--default-transition-duration: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
}
}
@layer base {
*, ::after, ::before, ::backdrop, ::file-selector-button {
box-sizing: border-box;
margin: 0;
padding: 0;
border: 0 solid;
}
html, :host {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
font-feature-settings: var(--default-font-feature-settings, normal);
font-variation-settings: var(--default-font-variation-settings, normal);
-webkit-tap-highlight-color: transparent;
}
hr {
height: 0;
color: inherit;
border-top-width: 1px;
}
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
h1, h2, h3, h4, h5, h6 {
font-size: inherit;
font-weight: inherit;
}
a {
color: inherit;
-webkit-text-decoration: inherit;
text-decoration: inherit;
}
b, strong {
font-weight: bolder;
}
code, kbd, samp, pre {
font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
font-feature-settings: var(--default-mono-font-feature-settings, normal);
font-variation-settings: var(--default-mono-font-variation-settings, normal);
font-size: 1em;
}
small {
font-size: 80%;
}
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
table {
text-indent: 0;
border-color: inherit;
border-collapse: collapse;
}
:-moz-focusring {
outline: auto;
}
progress {
vertical-align: baseline;
}
summary {
display: list-item;
}
ol, ul, menu {
list-style: none;
}
img, svg, video, canvas, audio, iframe, embed, object {
display: block;
/* vertical-align: middle; */
}
img, video {
max-width: 100%;
height: auto;
}
button, input, select, optgroup, textarea, ::file-selector-button {
font: inherit;
font-feature-settings: inherit;
font-variation-settings: inherit;
letter-spacing: inherit;
color: inherit;
border-radius: 0;
background-color: transparent;
opacity: 1;
}
:where(select:is([multiple], [size])) optgroup {
font-weight: bolder;
}
:where(select:is([multiple], [size])) optgroup option {
padding-inline-start: 20px;
}
::file-selector-button {
margin-inline-end: 4px;
}
::-moz-placeholder {
opacity: 1;
}
::placeholder {
opacity: 1;
}
@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
::-moz-placeholder {
color: currentcolor;
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, currentcolor 50%, transparent);
}
}
::placeholder {
color: currentcolor;
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, currentcolor 50%, transparent);
}
}
}
textarea {
resize: vertical;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-date-and-time-value {
min-height: 1lh;
text-align: inherit;
}
::-webkit-datetime-edit {
display: inline-flex;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
padding-block: 0;
}
:-moz-ui-invalid {
box-shadow: none;
}
button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
}
::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
height: auto;
}
[hidden]:where(:not([hidden="until-found"])) {
display: none !important;
}
}
@layer utilities {
.fixed {
position: fixed;
}
.static {
position: static;
}
.inset-0 {
inset: calc(var(--spacing) * 0);
}
.container {
width: 100%;
@media (width >= 40rem) {
max-width: 40rem;
}
@media (width >= 48rem) {
max-width: 48rem;
}
@media (width >= 64rem) {
max-width: 64rem;
}
@media (width >= 80rem) {
max-width: 80rem;
}
@media (width >= 96rem) {
max-width: 96rem;
}
}
.mx-auto {
margin-inline: auto;
}
.mt-2 {
margin-top: calc(var(--spacing) * 2);
}
.mt-4 {
margin-top: calc(var(--spacing) * 4);
}
.mb-1 {
margin-bottom: calc(var(--spacing) * 1);
}
.mb-2 {
margin-bottom: calc(var(--spacing) * 2);
}
.mb-4 {
margin-bottom: calc(var(--spacing) * 4);
}
.mb-6 {
margin-bottom: calc(var(--spacing) * 6);
}
.block {
display: block;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.hidden {
display: none;
}
.h-24 {
height: calc(var(--spacing) * 24);
}
.h-48 {
height: calc(var(--spacing) * 48);
}
.h-full {
height: 100%;
}
.min-h-full {
min-height: 100%;
}
.w-1\/3 {
width: calc(1/3 * 100%);
}
.w-2\/3 {
width: calc(2/3 * 100%);
}
.w-20 {
width: calc(var(--spacing) * 20);
}
.w-32 {
width: calc(var(--spacing) * 32);
}
.w-full {
width: 100%;
}
.max-w-md {
max-width: var(--container-md);
}
.flex-grow {
flex-grow: 1;
}
.transform {
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
}
.cursor-not-allowed {
cursor: not-allowed;
}
.resize {
resize: both;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.flex-col {
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.items-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}
.justify-end {
justify-content: flex-end;
}
.gap-2 {
gap: calc(var(--spacing) * 2);
}
.gap-4 {
gap: calc(var(--spacing) * 4);
}
.space-y-2 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
}
}
.space-y-4 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
}
}
.space-x-2 {
:where(& > :not(:last-child)) {
--tw-space-x-reverse: 0;
margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));
margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
}
}
.space-x-4 {
:where(& > :not(:last-child)) {
--tw-space-x-reverse: 0;
margin-inline-start: calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));
margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
}
}
.overflow-x-hidden {
overflow-x: hidden;
}
.overflow-y-auto {
overflow-y: auto;
}
.rounded {
border-radius: 0.25rem;
}
.rounded-lg {
border-radius: var(--radius-lg);
}
.border {
border-style: var(--tw-border-style);
border-width: 1px;
}
.bg-blue-600 {
background-color: var(--color-blue-600);
}
.bg-gray-600 {
background-color: var(--color-gray-600);
}
.bg-gray-700 {
background-color: var(--color-gray-700);
}
.bg-gray-800 {
background-color: var(--color-gray-800);
}
.bg-gray-900 {
background-color: var(--color-gray-900);
}
.bg-green-600 {
background-color: var(--color-green-600);
}
.bg-purple-600 {
background-color: var(--color-purple-600);
}
.bg-red-600 {
background-color: var(--color-red-600);
}
.bg-yellow-600 {
background-color: var(--color-yellow-600);
}
.p-4 {
padding: calc(var(--spacing) * 4);
}
.p-6 {
padding: calc(var(--spacing) * 6);
}
.p-8 {
padding: calc(var(--spacing) * 8);
}
.px-2 {
padding-inline: calc(var(--spacing) * 2);
}
.px-3 {
padding-inline: calc(var(--spacing) * 3);
}
.px-4 {
padding-inline: calc(var(--spacing) * 4);
}
.px-6 {
padding-inline: calc(var(--spacing) * 6);
}
.py-1 {
padding-block: calc(var(--spacing) * 1);
}
.py-2 {
padding-block: calc(var(--spacing) * 2);
}
.py-4 {
padding-block: calc(var(--spacing) * 4);
}
.text-center {
text-align: center;
}
.text-2xl {
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
.text-lg {
font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height));
}
.text-sm {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
}
.text-xl {
font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height));
}
.font-bold {
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
}
.font-medium {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
.font-semibold {
--tw-font-weight: var(--font-weight-semibold);
font-weight: var(--font-weight-semibold);
}
.text-blue-400 {
color: var(--color-blue-400);
}
.text-gray-400 {
color: var(--color-gray-400);
}
.text-red-500 {
color: var(--color-red-500);
}
.text-white {
color: var(--color-white);
}
.shadow-lg {
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.transition {
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.hover\:bg-blue-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-blue-700);
}
}
}
.hover\:bg-gray-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-gray-700);
}
}
}
.hover\:bg-green-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-green-700);
}
}
}
.hover\:bg-purple-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-purple-700);
}
}
}
.hover\:bg-red-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-red-700);
}
}
}
.hover\:bg-yellow-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-yellow-700);
}
}
}
.hover\:text-blue-500 {
&:hover {
@media (hover: hover) {
color: var(--color-blue-500);
}
}
}
.md\:grid-cols-2 {
@media (width >= 48rem) {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.md\:grid-cols-3 {
@media (width >= 48rem) {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
}
@layer base {
html {
height: 100%;
}
body {
min-height: 100%;
display: flex;
flex-direction: column;
}
#app {
flex: 1 0 auto;
display: flex;
flex-direction: column;
}
main {
flex-grow: 1;
}
footer {
flex-shrink: 0;
}
}
@layer components {
.spinner {
border: 4px solid rgba(255, 225, 225, 0.3);
border-top: 4px solid #ffffff;
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 1s linear infinite;
display: inline-block;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#notificationContainer {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}
.notification {
background-color: #1f2937;
color: white;
padding: 16px;
border-radius: 8px;
margin-top: 8px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: opacity 0.3s ease;
}
.notification.success {
background-color: #158106;
}
.notification.error {
background-color: #b91c1c;
}
#dockerLogsTerminal {
background-color: #1f2937;
padding: 1rem;
border-radius: 0.5rem;
max-height: 12rem;
width: 100%;
}
.xterm .xterm-viewport {
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #4b5563 #1f2937;
}
.xterm .xterm-viewport::-webkit-scrollbar {
width: 8px;
}
.xterm .xterm-viewport::-webkit-scrollbar-track {
background: #1f2937;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
.xterm .xterm-screen {
width: 100% !important;
}
.control-btn {
padding: 0.5rem 1rem;
font-size: 0.875rem;
line-height: 1.25rem;
transition: all 0.2s ease;
min-width: 80px;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.control-btn:hover:not(.disabled-btn) {
transform: translateY(-1px);
}
.control-btn:active:not(.disabled-btn) {
transform: translateY(0);
}
.modal {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.75);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: #1f2937;
padding: 1.5rem;
border-radius: 0.5rem;
width: 100%;
max-width: 600px;
position: relative;
max-height: 80vh;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #4b5563 #1f2937;
}
.modal-content::-webkit-scrollbar {
width: 8px;
}
.modal-content::-webkit-scrollbar-track {
background: #1f2937;
}
.modal-content::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
.modal-content::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
.modal-close {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: none;
border: none;
color: white;
font-size: 1.25rem;
cursor: pointer;
}
.disabled-btn {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
}
@layer utilities;
@media (max-width: 640px) {
.bg-gray-800.p-6.rounded-lg.shadow-lg .grid {
grid-template-columns: 1fr;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg p {
display: flex;
flex-direction: column;
gap: 0.5rem;
word-break: break-all;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg a,
.bg-gray-800.p-6.rounded-lg.shadow-lg span {
word-break: break-all;
overflow-wrap: anywhere;
white-space: normal;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg button {
width: 100%;
margin-top: 0.5rem;
}
}
.bg-gray-800.p-6.rounded-lg.shadow-lg .grid {
overflow-x: hidden;
}
.bg-gray-800.p-6.rounded-lg.shadow p {
max-width: 100%;
}
@property --tw-rotate-x {
syntax: "*";
inherits: false;
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
}
@property --tw-space-y-reverse {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-space-x-reverse {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-border-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-font-weight {
syntax: "*";
inherits: false;
}
@property --tw-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-shadow-color {
syntax: "*";
inherits: false;
}
@property --tw-shadow-alpha {
syntax: "<percentage>";
inherits: false;
initial-value: 100%;
}
@property --tw-inset-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-inset-shadow-color {
syntax: "*";
inherits: false;
}
@property --tw-inset-shadow-alpha {
syntax: "<percentage>";
inherits: false;
initial-value: 100%;
}
@property --tw-ring-color {
syntax: "*";
inherits: false;
}
@property --tw-ring-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-inset-ring-color {
syntax: "*";
inherits: false;
}
@property --tw-inset-ring-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-ring-inset {
syntax: "*";
inherits: false;
}
@property --tw-ring-offset-width {
syntax: "<length>";
inherits: false;
initial-value: 0px;
}
@property --tw-ring-offset-color {
syntax: "*";
inherits: false;
initial-value: #fff;
}
@property --tw-ring-offset-shadow {
syntax: "*";
inherits: false;
initial-value: 0 0 #0000;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
*, ::before, ::after, ::backdrop {
--tw-rotate-x: initial;
--tw-rotate-y: initial;
--tw-rotate-z: initial;
--tw-skew-x: initial;
--tw-skew-y: initial;
--tw-space-y-reverse: 0;
--tw-space-x-reverse: 0;
--tw-border-style: solid;
--tw-font-weight: initial;
--tw-shadow: 0 0 #0000;
--tw-shadow-color: initial;
--tw-shadow-alpha: 100%;
--tw-inset-shadow: 0 0 #0000;
--tw-inset-shadow-color: initial;
--tw-inset-shadow-alpha: 100%;
--tw-ring-color: initial;
--tw-ring-shadow: 0 0 #0000;
--tw-inset-ring-color: initial;
--tw-inset-ring-shadow: 0 0 #0000;
--tw-ring-inset: initial;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-offset-shadow: 0 0 #0000;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

BIN
public/favicon/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

221
public/index.html Normal file
View File

@ -0,0 +1,221 @@
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My-MC Server Panel</title>
<link rel="stylesheet" href="/css/styles.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" rel="stylesheet" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
<link rel="manifest" href="/favicon/site.webmanifest">
</head>
<body class="bg-gray-900 text-white overflow-x-hidden min-h-full flex flex-col">
<div id="app" class="flex-grow">
<div id="loginPage" class="hidden fixed inset-0 bg-gray-900 flex items-center justify-center">
<div class="bg-gray-800 p-8 rounded-lg shadow-lg w-full max-w-md">
<h2 class="text-2xl font-bold mb-6 text-center">My-MC Server Panel</h2>
<div class="mb-4">
<input id="loginApiKey" type="text" placeholder="Enter API Key"
class="bg-gray-700 px-4 py-2 rounded text-white w-full">
</div>
<button id="loginBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded w-full">Login</button>
<p id="loginError" class="text-red-500 text-sm mt-2 hidden"></p>
</div>
</div>
<div id="notificationContainer"></div>
<div id="tellModal" class="modal hidden">
<div class="modal-content">
<button class="modal-close">×</button>
<h2 class="text-xl font-semibold mb-4">Send Message to <span id="tellPlayerName"></span></h2>
<form id="tellForm">
<textarea id="tellMessage" placeholder="Enter your message"
class="bg-gray-700 px-4 py-2 rounded text-white w-full h-24 mb-4"></textarea>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded w-full">Send</button>
</form>
</div>
</div>
<div id="giveModal" class="modal hidden">
<div class="modal-content">
<button class="modal-close">×</button>
<h2 class="text-xl font-semibold mb-4">Give Items to <span id="givePlayerName"></span></h2>
<form id="giveForm">
<div class="mb-4">
<label for="loadoutSelect" class="block text-sm font-medium mb-1">Select Loadout</label>
<select id="loadoutSelect" class="bg-gray-700 px-4 py-2 rounded text-white w-full">
<option value="custom">Custom</option>
<option value="starter">Starter Kit (Torches, Food)</option>
<option value="builder">Builder Kit (Stone, Wood)</option>
<option value="combat">Combat Kit (Sword, Armor)</option>
<option value="miner">Miner Kit (Pickaxe, Torches, Shovel)</option>
<option value="adventurer">Adventurer Kit (Bow, Arrows, Compass)</option>
<option value="alchemist">Alchemist Kit (Potions, Brewing Stand)</option>
<option value="enchanter">Enchanter Kit (Books, Lapis, Enchanting Table)</option>
<option value="farmer">Farmer Kit (Seeds, Hoe, Bone Meal)</option>
<option value="nether">Nether Survival Kit (Fire Resistance, Obsidian)</option>
<option value="end">End Prep Kit (Ender Pearls, Blaze Rods)</option>
</select>
</div>
<div id="customGiveFields" class="hidden mb-4">
<div id="itemList" class="space-y-2"></div>
<button type="button" id="addItemBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded mt-2">Add
Item</button>
</div>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded w-full">Give</button>
</form>
</div>
</div>
<div id="editPropertiesModal" class="modal hidden">
<div class="modal-content">
<button class="modal-close">×</button>
<h2 class="text-xl font-semibold mb-4">Edit server.properties</h2>
<form id="editPropertiesForm" class="space-y-4">
<div id="propertiesFields" class="space-y-2"></div>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded w-full">Save</button>
</form>
</div>
</div>
<nav class="bg-gray-800 p-4 shadow-lg">
<div class="container mx-auto flex justify-between items-center">
<h1 class="text-2xl font-bold">My-MC Server Panel</h1>
<div class="flex space-x-4">
<button id="refresh" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded">Refresh</button>
<div id="authControls">
<input id="apiKey" type="text" placeholder="Enter API Key" class="bg-gray-700 px-4 py-2 rounded text-white">
</div>
</div>
</div>
</nav>
<main id="mainContent" class="container mx-auto p-6">
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6" data-section="server-status">
<h2 class="text-xl font-semibold mb-4">Server Status</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<p><strong>User:</strong> <span id="user">Loading...</span></p>
<p><strong>Key Expiry:</strong> <span id="keyExpiry">Loading...</span></p>
<p><strong>Status:</strong> <span id="serverStatus">Loading...</span></p>
</div>
<div class="flex space-x-4 justify-center">
<div class="text-center">
<canvas id="memoryMeter" width="150" height="150"></canvas>
<p class="text-sm mt-2">Memory Usage</p>
<p id="memoryPercent" class="text-lg font-bold">0%</p>
</div>
<div class="text-center">
<canvas id="cpuMeter" width="150" height="150"></canvas>
<p class="text-sm mt-2">CPU Usage</p>
<p id="cpuPercent" class="text-lg font-bold">0%</p>
</div>
</div>
<div class="flex flex-wrap space-x-2 justify-end items-center">
<div class="flex space-x-2">
<button id="startBtn"
class="bg-green-600 hover:bg-green-700 rounded font-medium control-btn">Start</button>
<button id="stopBtn" class="bg-red-600 hover:bg-red-700 rounded font-medium control-btn">Stop</button>
<button id="restartBtn"
class="bg-yellow-600 hover:bg-yellow-700 rounded font-medium control-btn">Restart</button>
</div>
<button id="editPropertiesBtn"
class="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded control-btn w-full mt-2">Edit Server
Properties</button>
</div>
</div>
</div>
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6">
<h2 class="text-xl font-semibold mb-4">Player Management</h2>
<div class="mb-4"></div>
<p><strong>Connected Players:</strong><br> <span id="playerList">Loading...</span></p>
</div>
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6">
<h2 class="text-xl font-semibold mb-4">Server Logs</h2>
<div id="dockerLogsTerminal" class="mt-4"></div>
</div>
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6">
<h2 class="text-xl font-semibold mb-4">Server Console</h2>
<form id="consoleForm" onsubmit="event.preventDefault(); sendConsoleCommand();">
<input id="consoleInput" type="text" placeholder="Enter RCON Command (Hit Enter To Submit)"
class="bg-gray-700 px-4 py-2 rounded text-white w-full mb-2">
<button id="sendConsole" type="submit" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded"
style="display: none;">Send</button>
</form>
<pre id="consoleOutput" class="bg-gray-900 p-4 rounded mt-4 h-48 overflow-y-auto"></pre>
</div>
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6">
<h2 class="text-xl font-semibold mb-4">Mod Management</h2>
<form id="modSearchForm" onsubmit="event.preventDefault(); searchMods(1);">
<div class="mb-4 flex space-x-2">
<input id="modSearch" type="text" placeholder="Search Mods (Hit Enter To Submit)"
class="bg-gray-700 px-4 py-2 rounded text-white flex-grow">
<button id="searchBtn" type="submit" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded"
style="display: none;">Search</button>
<button id="closeSearchBtn" type="button"
class="bg-gray-600 hover:bg-gray-700 px-4 py-2 rounded hidden">Close</button>
</div>
</form>
<div id="modResults" class="grid grid-cols-1 md:grid-cols-2 gap-4"></div>
<div id="pagination" class="mt-4 flex justify-center space-x-2"></div>
<h3 class="text-lg font-semibold mt-4">Installed Mods</h3>
<div id="modList" class="mt-2"></div>
</div>
<div class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6">
<h2 class="text-xl font-semibold mb-4">Server Links</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<p><strong>Advanced Log URL:</strong> <a id="logUrl" href="#" class="text-blue-400"
target="_blank">Loading...</a></p>
<p><strong>Website URL:</strong> <a id="websiteUrl" href="#" class="text-blue-400"
target="_blank">Loading...</a></p>
<p><strong>BlueMap URL:</strong> <a id="mapUrl" href="#" class="text-blue-400" target="_blank">Loading...</a>
</p>
<div>
<p><strong>Connection Link:</strong> <span id="myLink">Link Not Created</span> <span
id="connectionStatus"></span></p>
<button id="generateMyLinkBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-1 rounded mt-2">Generate
Connection Link</button>
</div>
<div>
<p><strong>Geyser Link:</strong> <span id="geyserLink">Link Not Created</span> <span
id="geyserStatus"></span></p>
<button id="generateGeyserLinkBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-1 rounded mt-2">Generate
Geyser Link</button>
</div>
<div>
<p><strong>SFTP Link:</strong> <span id="sftpLink">Link Not Created</span> <span id="sftpStatus"></span></p>
<button id="generateSftpLinkBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-1 rounded mt-2">Generate SFTP
Link</button>
</div>
</div>
</div>
</main>
<footer class="bg-gray-800 py-4">
<div class="container mx-auto px-6 text-center">
<p class="text-gray-400 text-sm">
© 2025 My-MC.Link. All rights reserved.<br>
<a href="https://raven-scott.fyi" target="_blank" class="text-blue-400 hover:text-blue-500">Made with ❤️ by
SNXRaven</a>
</p>
</div>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>