From cb6dbb8dd66039653bbb9ecff95d63afa8660ea1 Mon Sep 17 00:00:00 2001 From: MCHost Date: Sun, 6 Jul 2025 22:38:34 -0400 Subject: [PATCH] xterm theme --- public/css/main-site.min.css | 212 +++++++++++++++++++++++++++++++++++ public/css/style.css | 57 +++++++++- public/css/style.min.css | 41 ++++++- public/js/app.js | 72 +++++++++--- 4 files changed, 364 insertions(+), 18 deletions(-) diff --git a/public/css/main-site.min.css b/public/css/main-site.min.css index cbd7a2e..61549a4 100644 --- a/public/css/main-site.min.css +++ b/public/css/main-site.min.css @@ -54,6 +54,8 @@ --text-2xl--line-height: calc(2 / 1.5); --text-3xl: 1.875rem; --text-3xl--line-height: calc(2.25 / 1.875); + --text-4xl: 2.25rem; + --text-4xl--line-height: calc(2.5 / 2.25); --text-5xl: 3rem; --text-5xl--line-height: 1; --font-weight-medium: 500; @@ -221,6 +223,9 @@ } } @layer utilities { + .pointer-events-none { + pointer-events: none; + } .absolute { position: absolute; } @@ -236,12 +241,39 @@ .inset-0 { inset: calc(var(--spacing) * 0); } + .top-1 { + top: calc(var(--spacing) * 1); + } + .top-2 { + top: calc(var(--spacing) * 2); + } + .top-4 { + top: calc(var(--spacing) * 4); + } + .right-2 { + right: calc(var(--spacing) * 2); + } + .right-4 { + right: calc(var(--spacing) * 4); + } + .bottom-4 { + bottom: calc(var(--spacing) * 4); + } + .left-1 { + left: calc(var(--spacing) * 1); + } .z-2 { z-index: 2; } .z-50 { z-index: 50; } + .z-\[1000\] { + z-index: 1000; + } + .z-\[99998\] { + z-index: 99998; + } .container { width: 100%; @media (width >= 40rem) { @@ -323,33 +355,66 @@ .inline-block { display: inline-block; } + .table { + display: table; + } + .h-0 { + height: calc(var(--spacing) * 0); + } .h-0\.5 { height: calc(var(--spacing) * 0.5); } .h-4 { height: calc(var(--spacing) * 4); } + .h-6 { + height: calc(var(--spacing) * 6); + } .h-10 { height: calc(var(--spacing) * 10); } .h-24 { height: calc(var(--spacing) * 24); } + .h-28 { + height: calc(var(--spacing) * 28); + } + .h-32 { + height: calc(var(--spacing) * 32); + } .h-48 { height: calc(var(--spacing) * 48); } + .h-\[30vh\] { + height: 30vh; + } + .h-\[35vh\] { + height: 35vh; + } + .h-\[40vh\] { + height: 40vh; + } .h-full { height: 100%; } + .max-h-\[80vh\] { + max-height: 80vh; + } .max-h-\[calc\(95vh-10rem\)\] { max-height: calc(95vh - 10rem); } .min-h-full { min-height: 100%; } + .w-1 { + width: calc(var(--spacing) * 1); + } .w-1\/3 { width: calc(1/3 * 100%); } + .w-2 { + width: calc(var(--spacing) * 2); + } .w-2\/3 { width: calc(2/3 * 100%); } @@ -362,6 +427,9 @@ .w-10 { width: calc(var(--spacing) * 10); } + .w-16 { + width: calc(var(--spacing) * 16); + } .w-20 { width: calc(var(--spacing) * 20); } @@ -374,9 +442,18 @@ .max-w-7xl { max-width: var(--container-7xl); } + .max-w-\[150px\] { + max-width: 150px; + } + .max-w-full { + max-width: 100%; + } .max-w-md { max-width: var(--container-md); } + .max-w-xl { + max-width: var(--container-xl); + } .min-w-0 { min-width: calc(var(--spacing) * 0); } @@ -389,9 +466,23 @@ .flex-grow { flex-grow: 1; } + .border-collapse { + border-collapse: collapse; + } + .-translate-y-2 { + --tw-translate-y: calc(var(--spacing) * -2); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-y-2 { + --tw-translate-y: calc(var(--spacing) * 2); + 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-spin { + animation: var(--animate-spin); + } .cursor-not-allowed { cursor: not-allowed; } @@ -413,6 +504,9 @@ .flex-col { flex-direction: column; } + .flex-col-reverse { + flex-direction: column-reverse; + } .flex-row { flex-direction: row; } @@ -434,6 +528,9 @@ .justify-end { justify-content: flex-end; } + .gap-1 { + gap: calc(var(--spacing) * 1); + } .gap-2 { gap: calc(var(--spacing) * 2); } @@ -514,6 +611,18 @@ border-style: var(--tw-border-style); border-width: 1px; } + .border-1 { + border-style: var(--tw-border-style); + border-width: 1px; + } + .border-2 { + border-style: var(--tw-border-style); + border-width: 2px; + } + .border-4 { + border-style: var(--tw-border-style); + border-width: 4px; + } .border-t { border-top-style: var(--tw-border-style); border-top-width: 1px; @@ -522,12 +631,25 @@ border-bottom-style: var(--tw-border-style); border-bottom-width: 1px; } + .border-none { + --tw-border-style: none; + border-style: none; + } .border-\[rgba\(255\,255\,255\,0\.12\)\] { border-color: rgba(255,255,255,0.12); } .border-\[rgba\(255\,255\,255\,0\.15\)\] { border-color: rgba(255,255,255,0.15); } + .border-blue-400 { + border-color: var(--color-blue-400); + } + .border-gray-700 { + border-color: var(--color-gray-700); + } + .border-t-blue-600 { + border-top-color: var(--color-blue-600); + } .bg-\[rgba\(10\,17\,40\,0\.3\)\] { background-color: rgba(10,17,40,0.3); } @@ -537,9 +659,21 @@ .bg-black { background-color: var(--color-black); } + .bg-black\/75 { + background-color: color-mix(in srgb, #000 75%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-black) 75%, transparent); + } + } + .bg-blue-400 { + background-color: var(--color-blue-400); + } .bg-blue-600 { background-color: var(--color-blue-600); } + .bg-blue-800 { + background-color: var(--color-blue-800); + } .bg-cyan-600 { background-color: var(--color-cyan-600); } @@ -558,6 +692,9 @@ background-color: color-mix(in oklab, var(--color-gray-800) 40%, transparent); } } + .bg-gray-900 { + background-color: var(--color-gray-900); + } .bg-green-600 { background-color: var(--color-green-600); } @@ -573,13 +710,27 @@ .bg-teal-600 { background-color: var(--color-teal-600); } + .bg-transparent { + background-color: transparent; + } .bg-white { background-color: var(--color-white); } + .bg-yellow-500 { + background-color: var(--color-yellow-500); + } .bg-gradient-to-r { --tw-gradient-position: to right in oklab; background-image: linear-gradient(var(--tw-gradient-stops)); } + .from-blue-600 { + --tw-gradient-from: var(--color-blue-600); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + .from-blue-700 { + --tw-gradient-from: var(--color-blue-700); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } .from-teal-400 { --tw-gradient-from: var(--color-teal-400); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); @@ -591,6 +742,9 @@ .bg-clip-text { background-clip: text; } + .p-1 { + padding: calc(var(--spacing) * 1); + } .p-2 { padding: calc(var(--spacing) * 2); } @@ -615,12 +769,21 @@ .px-4 { padding-inline: calc(var(--spacing) * 4); } + .py-0 { + padding-block: calc(var(--spacing) * 0); + } + .py-0\.5 { + padding-block: calc(var(--spacing) * 0.5); + } .py-1 { padding-block: calc(var(--spacing) * 1); } .py-2 { padding-block: calc(var(--spacing) * 2); } + .py-4 { + padding-block: calc(var(--spacing) * 4); + } .py-8 { padding-block: calc(var(--spacing) * 8); } @@ -636,6 +799,9 @@ .text-center { text-align: center; } + .font-\[Minecraft\] { + font-family: Minecraft; + } .text-2xl { font-size: var(--text-2xl); line-height: var(--tw-leading, var(--text-2xl--line-height)); @@ -644,6 +810,10 @@ font-size: var(--text-3xl); line-height: var(--tw-leading, var(--text-3xl--line-height)); } + .text-4xl { + font-size: var(--text-4xl); + line-height: var(--tw-leading, var(--text-4xl--line-height)); + } .text-5xl { font-size: var(--text-5xl); line-height: var(--tw-leading, var(--text-5xl--line-height)); @@ -730,10 +900,21 @@ .opacity-90 { opacity: 90%; } + .opacity-100 { + opacity: 100%; + } + .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 { --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); } + .outline { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } .filter { filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } @@ -836,6 +1017,13 @@ } } } + .hover\:bg-yellow-600 { + &:hover { + @media (hover: hover) { + background-color: var(--color-yellow-600); + } + } + } .hover\:bg-gradient-to-r { &:hover { @media (hover: hover) { @@ -1463,6 +1651,21 @@ footer a:hover { padding: 0.75rem 1.5rem; } } +@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; @@ -1617,6 +1820,11 @@ footer a:hover { inherits: false; initial-value: 0 0 #0000; } +@property --tw-outline-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} @property --tw-blur { syntax: "*"; inherits: false; @@ -1722,6 +1930,9 @@ footer a:hover { @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-translate-x: 0; + --tw-translate-y: 0; + --tw-translate-z: 0; --tw-rotate-x: initial; --tw-rotate-y: initial; --tw-rotate-z: initial; @@ -1756,6 +1967,7 @@ footer a:hover { --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; + --tw-outline-style: solid; --tw-blur: initial; --tw-brightness: initial; --tw-contrast: initial; diff --git a/public/css/style.css b/public/css/style.css index 3d11f67..2bf006d 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -40,7 +40,61 @@ } .xterm { - background-color: #111426 !important; + /* Ensure terminal container respects parent dimensions */ + width: 100%; + height: 100%; + padding: 8px; + /* Add padding for better text spacing */ + box-sizing: border-box; + /* Prevent padding from causing overflow */ + } + + /* Viewport styling for scrollbars */ + .xterm .xterm-viewport { + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: #4b5563 #1f2937; + } + + /* Webkit scrollbar styling */ + .xterm .xterm-viewport::-webkit-scrollbar { + width: 8px; + } + + .xterm .xterm-viewport::-webkit-scrollbar-track { + background: #1f2937; + /* Match terminal background for consistency */ + } + + .xterm .xterm-viewport::-webkit-scrollbar-thumb { + background: #4b5563; + border-radius: 4px; + } + + .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { + background: #6b7280; + /* Slightly lighter on hover for feedback */ + } + + /* Ensure screen fits container */ + .xterm .xterm-screen { + width: 100% !important; + height: 100% !important; + /* Ensure full height usage */ + } + + /* Improve text rendering */ + .xterm .xterm-char { + line-height: 1.2; + /* Adjust line height for better text alignment */ + } + + /* Prevent text overflow */ + .xterm .xterm-rows { + overflow: hidden; + /* Prevent horizontal overflow */ + white-space: pre-wrap; + /* Allow text wrapping if needed */ } #app { @@ -254,6 +308,7 @@ .nav-btn { @apply bg-blue-600 text-white font-medium px-3 py-1 rounded transition duration-200 text-sm; } + .nav-btn2 { @apply bg-blue-600 text-white font-medium px-3 py-1 rounded transition duration-200 text-sm; } diff --git a/public/css/style.min.css b/public/css/style.min.css index e493d79..a5ec6a6 100644 --- a/public/css/style.min.css +++ b/public/css/style.min.css @@ -54,6 +54,8 @@ --text-2xl--line-height: calc(2 / 1.5); --text-3xl: 1.875rem; --text-3xl--line-height: calc(2.25 / 1.875); + --text-4xl: 2.25rem; + --text-4xl--line-height: calc(2.5 / 2.25); --text-5xl: 3rem; --text-5xl--line-height: 1; --font-weight-medium: 500; @@ -65,6 +67,7 @@ --radius-md: 0.375rem; --radius-lg: 0.5rem; --radius-xl: 0.75rem; + --ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); --animate-spin: spin 1s linear infinite; --blur-xl: 24px; @@ -782,6 +785,10 @@ --tw-ease: var(--ease-in-out); transition-timing-function: var(--ease-in-out); } + .ease-out { + --tw-ease: var(--ease-out); + transition-timing-function: var(--ease-out); + } .hover\:bg-blue-700 { &:hover { @media (hover: hover) { @@ -954,7 +961,39 @@ letter-spacing: 0.5px; } .xterm { - background-color: #111426 !important; + width: 100%; + height: 100%; + padding: 8px; + box-sizing: border-box; + } + .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; + height: 100% !important; + } + .xterm .xterm-char { + line-height: 1.2; + } + .xterm .xterm-rows { + overflow: hidden; + white-space: pre-wrap; } #app { flex-grow: 1; diff --git a/public/js/app.js b/public/js/app.js index 8873453..38232d7 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -390,35 +390,75 @@ document.addEventListener('DOMContentLoaded', () => { } function initializeTerminal() { + const terminalElement = document.getElementById('dockerLogsTerminal'); + + // Early return if terminal element is not found + if (!terminalElement) { + console.error('Terminal element not found'); + return; + } + + // Reuse existing terminal instance if available if (terminal) { terminal.clear(); + terminal.reset(); } else { + // Lazy-load xterm and addons to reduce initial bundle size terminal = new Terminal({ - rows: 22, - fontSize: 14, - fontFamily: 'monospace', + rows: 50, + fontSize: 12, + fontFamily: '"Fira Mono", monospace', theme: { - background: '#1f2937', + background: '#121528', foreground: '#ffffff', - cursor: '#ffffff' + cursor: '#ffffff', + selectionBackground: '#ffffff44', // Improve accessibility }, - scrollback: 1000, - rendererType: 'canvas' + scrollback: 5000, // Reduced for better memory usage + rendererType: 'dom', // Use 'dom' for better performance in most cases + cursorBlink: false, // Disable for better performance + convertEol: true, // Normalize line endings for consistency }); + + // Initialize FitAddon fitAddon = new FitAddon.FitAddon(); terminal.loadAddon(fitAddon); - terminal.open(elements.dockerLogsTerminal); - terminal.element.style.border = 'none'; + + // Open terminal and remove border + terminal.open(terminalElement); + terminalElement.style.border = 'none'; } + + // Fit terminal to container fitAddon.fit(); - - window.addEventListener('resize', () => { - if (fitAddon && terminal) { - fitAddon.fit(); - } - }); + + // Debounced resize handler to prevent excessive reflows + let resizeTimeout; + const debouncedResize = () => { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(() => { + if (fitAddon && terminal) { + fitAddon.fit(); + } + }, 100); + }; + + // Clean up previous event listeners to prevent memory leaks + window.removeEventListener('resize', debouncedResize); + window.addEventListener('resize', debouncedResize); + + // Return terminal instance for external use if needed + return terminal; + } + + // Optional: Clean up terminal when no longer needed + function disposeTerminal() { + if (terminal) { + terminal.dispose(); + terminal = null; + fitAddon = null; + } } - function connectWebSocket() { if (ws && ws.readyState === WebSocket.OPEN) { return;