update to add widget section

This commit is contained in:
MCHost
2025-07-03 02:51:10 -04:00
parent 827799b7cb
commit c464f3b3b5
5 changed files with 180 additions and 34 deletions

View File

@ -59,7 +59,32 @@
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin: 0 auto; margin: 0 auto;
} }
@keyframes spin { @keyframes spin {
0% { transform: rotate(0deg); } 0% {
100% { transform: rotate(360deg); } transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Add fade-out animation for the banner alert */
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
#copyAlert {
transition: opacity 0.3s ease-out;
}
#copyAlert.hidden {
opacity: 0;
} }

View File

@ -8,49 +8,25 @@
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace; "Courier New", monospace;
--color-red-400: oklch(70.4% 0.191 22.216); --color-red-400: oklch(70.4% 0.191 22.216);
--color-red-500: oklch(63.7% 0.237 25.331); --color-yellow-400: oklch(85.2% 0.199 91.936);
--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-400: oklch(79.2% 0.209 151.711); --color-green-400: oklch(79.2% 0.209 151.711);
--color-green-600: oklch(62.7% 0.194 149.214);
--color-green-700: oklch(52.7% 0.154 150.069);
--color-teal-600: oklch(60% 0.118 184.704);
--color-teal-700: oklch(51.1% 0.096 186.391);
--color-cyan-600: oklch(60.9% 0.126 221.723);
--color-cyan-700: oklch(52% 0.105 223.128);
--color-blue-400: oklch(70.7% 0.165 254.624); --color-blue-400: oklch(70.7% 0.165 254.624);
--color-blue-500: oklch(62.3% 0.214 259.815); --color-blue-500: oklch(62.3% 0.214 259.815);
--color-blue-600: oklch(54.6% 0.245 262.881); --color-blue-600: oklch(54.6% 0.245 262.881);
--color-blue-700: oklch(48.8% 0.243 264.376); --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-100: oklch(96.7% 0.003 264.542);
--color-gray-200: oklch(92.8% 0.006 264.531); --color-gray-200: oklch(92.8% 0.006 264.531);
--color-gray-300: oklch(87.2% 0.01 258.338); --color-gray-300: oklch(87.2% 0.01 258.338);
--color-gray-400: oklch(70.7% 0.022 261.325); --color-gray-400: oklch(70.7% 0.022 261.325);
--color-gray-500: oklch(55.1% 0.027 264.364);
--color-gray-600: oklch(44.6% 0.03 256.802); --color-gray-600: oklch(44.6% 0.03 256.802);
--color-gray-700: oklch(37.3% 0.034 259.733); --color-gray-700: oklch(37.3% 0.034 259.733);
--color-gray-800: oklch(27.8% 0.033 256.848); --color-gray-800: oklch(27.8% 0.033 256.848);
--color-gray-900: oklch(21% 0.034 264.665); --color-gray-900: oklch(21% 0.034 264.665);
--color-black: #000;
--color-white: #fff; --color-white: #fff;
--spacing: 0.25rem; --spacing: 0.25rem;
--container-md: 28rem;
--container-lg: 32rem; --container-lg: 32rem;
--container-7xl: 80rem; --container-7xl: 80rem;
--text-xs: 0.75rem;
--text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem; --text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875); --text-sm--line-height: calc(1.25 / 0.875);
--text-base: 1rem;
--text-base--line-height: calc(1.5 / 1);
--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: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5); --text-2xl--line-height: calc(2 / 1.5);
--text-4xl: 2.25rem; --text-4xl: 2.25rem;
@ -59,12 +35,9 @@
--font-weight-semibold: 600; --font-weight-semibold: 600;
--font-weight-bold: 700; --font-weight-bold: 700;
--tracking-tight: -0.025em; --tracking-tight: -0.025em;
--leading-relaxed: 1.625;
--radius-md: 0.375rem; --radius-md: 0.375rem;
--radius-lg: 0.5rem; --radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--radius-2xl: 1rem; --radius-2xl: 1rem;
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--blur-md: 12px; --blur-md: 12px;
--default-transition-duration: 150ms; --default-transition-duration: 150ms;
@ -219,21 +192,36 @@
} }
} }
@layer utilities { @layer utilities {
.visible {
visibility: visible;
}
.absolute { .absolute {
position: absolute; position: absolute;
} }
.fixed {
position: fixed;
}
.relative { .relative {
position: relative; position: relative;
} }
.inset-0 { .inset-0 {
inset: calc(var(--spacing) * 0); inset: calc(var(--spacing) * 0);
} }
.top-4 {
top: calc(var(--spacing) * 4);
}
.left-1\/2 {
left: calc(1/2 * 100%);
}
.z-0 { .z-0 {
z-index: 0; z-index: 0;
} }
.z-10 { .z-10 {
z-index: 10; z-index: 10;
} }
.z-50 {
z-index: 50;
}
.mx-auto { .mx-auto {
margin-inline: auto; margin-inline: auto;
} }
@ -246,6 +234,9 @@
.mt-8 { .mt-8 {
margin-top: calc(var(--spacing) * 8); margin-top: calc(var(--spacing) * 8);
} }
.mb-4 {
margin-bottom: calc(var(--spacing) * 4);
}
.block { .block {
display: block; display: block;
} }
@ -270,6 +261,13 @@
.flex-grow { .flex-grow {
flex-grow: 1; flex-grow: 1;
} }
.-translate-x-1\/2 {
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.transform {
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
}
.animate-pulse { .animate-pulse {
animation: var(--animate-pulse); animation: var(--animate-pulse);
} }
@ -286,12 +284,18 @@
.overflow-hidden { .overflow-hidden {
overflow: hidden; overflow: hidden;
} }
.overflow-x-auto {
overflow-x: auto;
}
.rounded-2xl { .rounded-2xl {
border-radius: var(--radius-2xl); border-radius: var(--radius-2xl);
} }
.rounded-lg { .rounded-lg {
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
} }
.rounded-md {
border-radius: var(--radius-md);
}
.border { .border {
border-style: var(--tw-border-style); border-style: var(--tw-border-style);
border-width: 1px; border-width: 1px;
@ -308,6 +312,9 @@
background-color: color-mix(in oklab, var(--color-gray-700) 50%, transparent); background-color: color-mix(in oklab, var(--color-gray-700) 50%, transparent);
} }
} }
.bg-gray-800 {
background-color: var(--color-gray-800);
}
.bg-gray-800\/50 { .bg-gray-800\/50 {
background-color: color-mix(in srgb, oklch(27.8% 0.033 256.848) 50%, transparent); background-color: color-mix(in srgb, oklch(27.8% 0.033 256.848) 50%, transparent);
@supports (color: color-mix(in lab, red, red)) { @supports (color: color-mix(in lab, red, red)) {
@ -323,6 +330,9 @@
.bg-gray-900 { .bg-gray-900 {
background-color: var(--color-gray-900); background-color: var(--color-gray-900);
} }
.p-4 {
padding: calc(var(--spacing) * 4);
}
.p-6 { .p-6 {
padding: calc(var(--spacing) * 6); padding: calc(var(--spacing) * 6);
} }
@ -335,6 +345,9 @@
.px-4 { .px-4 {
padding-inline: calc(var(--spacing) * 4); padding-inline: calc(var(--spacing) * 4);
} }
.px-6 {
padding-inline: calc(var(--spacing) * 6);
}
.py-2 { .py-2 {
padding-block: calc(var(--spacing) * 2); padding-block: calc(var(--spacing) * 2);
} }
@ -402,6 +415,13 @@
.text-white { .text-white {
color: var(--color-white); color: var(--color-white);
} }
.text-yellow-400 {
color: var(--color-yellow-400);
}
.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);
}
.shadow-md { .shadow-md {
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px 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); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@ -435,6 +455,10 @@
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
} }
.backdrop-filter {
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
}
.transition-all { .transition-all {
transition-property: all; transition-property: all;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@ -545,6 +569,55 @@
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
#copyAlert {
transition: opacity 0.3s ease-out;
}
#copyAlert.hidden {
opacity: 0;
}
@property --tw-translate-x {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-translate-y {
syntax: "*";
inherits: false;
initial-value: 0;
}
@property --tw-translate-z {
syntax: "*";
inherits: false;
initial-value: 0;
}
@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 { @property --tw-space-y-reverse {
syntax: "*"; syntax: "*";
inherits: false; inherits: false;
@ -681,6 +754,14 @@
@layer properties { @layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { @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 { *, ::before, ::after, ::backdrop {
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-translate-z: 0;
--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-y-reverse: 0;
--tw-border-style: solid; --tw-border-style: solid;
--tw-font-weight: initial; --tw-font-weight: initial;

View File

@ -51,6 +51,21 @@
<h2 class="text-2xl font-semibold text-blue-400 animate-slide-in">Server Status</h2> <h2 class="text-2xl font-semibold text-blue-400 animate-slide-in">Server Status</h2>
<div id="statusContent" class="mt-4 p-6 bg-gray-700/50 rounded-lg text-gray-200 ring-1 ring-gray-600/50"></div> <div id="statusContent" class="mt-4 p-6 bg-gray-700/50 rounded-lg text-gray-200 ring-1 ring-gray-600/50"></div>
</div> </div>
<div id="widgetSection" class="mt-8 hidden">
<h2 class="text-2xl font-semibold text-blue-400 animate-slide-in">Embed Server Status Widget</h2>
<div class="mt-4 p-6 bg-gray-700/50 rounded-lg text-gray-200 ring-1 ring-gray-600/50">
<p class="mb-4">Add a widget to your website to display your server's status:</p>
<pre id="widgetCode" class="bg-gray-800 p-4 rounded-md text-sm text-gray-300 overflow-x-auto" style="display: none;"></pre>
<button id="copyWidgetCode" class="mt-4 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg transition-all duration-300 shadow-md hover:shadow-lg">
Copy Widget Code
</button>
<div id="copyAlert" class="hidden fixed bottom-4 right-4 bg-blue-600 text-white text-sm px-6 py-3 rounded-lg shadow-lg animate-slide-in z-50">
Widget code copied to clipboard!
</div>
</div>
</div>
</div> </div>
</main> </main>
<footer class="relative z-10 bg-gray-800/80 backdrop-blur-md py-4 shadow-md"> <footer class="relative z-10 bg-gray-800/80 backdrop-blur-md py-4 shadow-md">
@ -58,6 +73,9 @@
<p class="text-gray-400 text-sm">© 2025 My-MC.Link Server Status Checker</p> <p class="text-gray-400 text-sm">© 2025 My-MC.Link Server Status Checker</p>
</div> </div>
</footer> </footer>
<script>
</script>
<script src="js/app.js"></script> <script src="js/app.js"></script>
</body> </body>

View File

@ -34,6 +34,8 @@ document.getElementById('serverForm').addEventListener('submit', async (e) => {
const loadingSpinner = document.getElementById('loadingSpinner'); const loadingSpinner = document.getElementById('loadingSpinner');
const statusResult = document.getElementById('statusResult'); const statusResult = document.getElementById('statusResult');
const statusContent = document.getElementById('statusContent'); const statusContent = document.getElementById('statusContent');
const widgetSection = document.getElementById('widgetSection');
const widgetCode = document.getElementById('widgetCode');
const submitButton = e.target.querySelector('button[type="submit"]'); const submitButton = e.target.querySelector('button[type="submit"]');
if (!host || !port) { if (!host || !port) {
@ -45,6 +47,7 @@ document.getElementById('serverForm').addEventListener('submit', async (e) => {
loadingSpinner.style.display = 'block'; loadingSpinner.style.display = 'block';
submitButton.disabled = true; submitButton.disabled = true;
statusResult.classList.add('hidden'); statusResult.classList.add('hidden');
widgetSection.classList.add('hidden');
statusContent.innerHTML = '<p class="animate-pulse">Checking...</p>'; statusContent.innerHTML = '<p class="animate-pulse">Checking...</p>';
try { try {
@ -88,6 +91,11 @@ document.getElementById('serverForm').addEventListener('submit', async (e) => {
<p><strong class="text-blue-400">${key}:</strong> ${value}</p> <p><strong class="text-blue-400">${key}:</strong> ${value}</p>
`).join('')} `).join('')}
`; `;
// Show widget section and set widget code
widgetSection.classList.remove('hidden');
const widgetIframe = `<iframe src="https://status.my-mc.link/widget/${edition}/${host}/${port}" width="280" height="145" frameborder="0" scrolling="no"></iframe>`;
widgetCode.textContent = widgetIframe;
} else { } else {
statusContent.innerHTML = ` statusContent.innerHTML = `
<p><strong class="text-blue-400">Status:</strong> <span class="text-red-400">Offline</span></p> <p><strong class="text-blue-400">Status:</strong> <span class="text-red-400">Offline</span></p>
@ -114,6 +122,19 @@ document.getElementById('serverForm').addEventListener('submit', async (e) => {
} }
}); });
// Assuming the copy functionality is in js/app.js, add this to handle the banner alert
document.getElementById('copyWidgetCode').addEventListener('click', function () {
const widgetCode = document.getElementById('widgetCode').textContent;
navigator.clipboard.writeText(widgetCode).then(() => {
const alert = document.getElementById('copyAlert');
alert.classList.remove('hidden');
setTimeout(() => {
alert.classList.add('hidden');
}, 3000);
}).catch(err => {
console.error('Failed to copy: ', err);
});
});
// Handle URL-based checks // Handle URL-based checks
const path = window.location.pathname.split('/'); const path = window.location.pathname.split('/');
if (path[1] && path[2] && path[3]) { if (path[1] && path[2] && path[3]) {

View File

@ -406,10 +406,11 @@ app.get('/widget/:edition/:host/:port', (req, res) => {
try { try {
validateEdition(edition); validateEdition(edition);
validateHostname(host); validateHostname(host);
if (host !== 'my-mc.link') { // Uncomment this to only allow our system servers to have widgets.
logger.error('Unauthorized hostname', { host }); // if (host !== 'my-mc.link') {
throw new Error('Hostname must be my-mc.link'); // logger.error('Unauthorized hostname', { host });
} // throw new Error('Hostname must be my-mc.link');
// }
validatePort(port); validatePort(port);
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
res.send(widgetTemplate(edition, host, port)); res.send(widgetTemplate(edition, host, port));