diff --git a/scifi-ui/index.html b/scifi-ui/index.html index 4036537..e246220 100644 --- a/scifi-ui/index.html +++ b/scifi-ui/index.html @@ -11,6 +11,7 @@ + @@ -69,6 +70,11 @@

.NET Desktop Runtime 10.0 (x64)

Web UI

Böngészőben futó felület • demo

+ + +

Licenc

+

Licencfeltételek • LICENSE.txt

+
@@ -99,5 +105,6 @@

Dokumentumok

+ \ No newline at end of file diff --git a/scifi-ui/scripts/holographic-panel.js b/scifi-ui/scripts/holographic-panel.js new file mode 100644 index 0000000..052d53f --- /dev/null +++ b/scifi-ui/scripts/holographic-panel.js @@ -0,0 +1,252 @@ +(function () { + 'use strict'; + + const THEMES = ['lcars', 'holo', 'cyber']; + + /** + * Detects the current theme from body classes + */ + function getCurrentTheme() { + const body = document.body; + for (const theme of THEMES) { + if (body.classList.contains(`theme-${theme}`)) { + return theme; + } + } + return 'lcars'; // default + } + + /** + * Checks if user prefers reduced motion + */ + function prefersReducedMotion() { + return window.matchMedia('(prefers-reduced-motion: reduce)').matches; + } + + /** + * Finds the LICENSE section/card in the DOM + * Priority: + * 1. Look for dedicated container: #license, .license, [data-section="license"] + * 2. Search for tags with LICENSE/LICENCE in href or textContent + */ + function findLicenseElement() { + // Try dedicated containers first + const dedicatedSelectors = ['#license', '.license', '[data-section="license"]']; + for (const selector of dedicatedSelectors) { + const el = document.querySelector(selector); + if (el) return el; + } + + // Search all anchor tags + const links = Array.from(document.querySelectorAll('a')); + for (const link of links) { + const href = link.getAttribute('href') || ''; + const text = link.textContent || ''; + + // Check if href ends with LICENSE/LICENCE (optionally .txt) + if (/licen[cs]e(\.txt)?$/i.test(href)) { + // Return the closest card or section container + return link.closest('.card') || link.closest('section') || link; + } + + // Check if text content matches license pattern + if (/licen[cs]e/i.test(text)) { + return link.closest('.card') || link.closest('section') || link; + } + } + + return null; + } + + /** + * Creates the holographic panel HTML based on theme + */ + function createHolographicPanel(theme) { + const panel = document.createElement('div'); + panel.className = 'holographic-panel'; + panel.setAttribute('data-theme', theme); + panel.setAttribute('aria-label', 'Holographic display panel'); + + const reducedMotion = prefersReducedMotion(); + if (reducedMotion) { + panel.setAttribute('data-reduced-motion', 'true'); + } + + let content = ''; + + if (theme === 'lcars') { + // LCARS: Subspace telemetry radar sweep + content = ` +
+ + + + + + + + + + + + + + + + + + + + + +
SUBSPACE TELEMETRY
+
+ `; + } else if (theme === 'holo') { + // Star Wars: Holonet rings (3D-ish) + content = ` +
+ + + + + + + + + + + + + + + + + + + + +
HOLONET TRANSMISSION
+
+ `; + } else if (theme === 'cyber') { + // Cyberpunk: Neon glitch with grid scan + content = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NEURAL LINK ACTIVE
+
+ `; + } + + panel.innerHTML = content; + return panel; + } + + /** + * Inserts the holographic panel after the LICENSE element + */ + function insertHolographicPanel() { + const theme = getCurrentTheme(); + const licenseElement = findLicenseElement(); + + if (!licenseElement) { + console.warn('LICENSE element not found. Appending holographic panel to end of main.'); + const main = document.querySelector('main'); + if (main) { + const panel = createHolographicPanel(theme); + main.appendChild(panel); + } + return; + } + + // Check if panel already exists + const existingPanel = document.querySelector('.holographic-panel'); + if (existingPanel) { + existingPanel.remove(); + } + + const panel = createHolographicPanel(theme); + + // Check if license element is inside a .download-grid + const grid = licenseElement.closest('.download-grid'); + + if (grid) { + // Insert panel after the entire grid, not inside it + grid.insertAdjacentElement('afterend', panel); + } else { + // Fallback: Insert after the license element itself + if (licenseElement.nextSibling) { + licenseElement.parentNode.insertBefore(panel, licenseElement.nextSibling); + } else { + licenseElement.parentNode.appendChild(panel); + } + } + + // Safety fallback: if panel ends up in a grid, span all columns + if (panel.parentElement && panel.parentElement.classList.contains('download-grid')) { + panel.style.gridColumn = '1 / -1'; + } + } + + /** + * Updates the panel when theme changes + */ + function updatePanelForTheme() { + insertHolographicPanel(); + } + + // Initialize on DOM ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', insertHolographicPanel); + } else { + insertHolographicPanel(); + } + + // Listen for theme changes (the theme switcher in site.js doesn't dispatch events, + // so we'll observe class changes on body) + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + updatePanelForTheme(); + break; + } + } + }); + + observer.observe(document.body, { + attributes: true, + attributeFilter: ['class'] + }); + + // Also listen for reduced motion changes + const motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + motionQuery.addEventListener('change', updatePanelForTheme); +})(); diff --git a/scifi-ui/styles/holographic-panel.css b/scifi-ui/styles/holographic-panel.css new file mode 100644 index 0000000..315c12b --- /dev/null +++ b/scifi-ui/styles/holographic-panel.css @@ -0,0 +1,315 @@ +/* Holographic Panel Styles */ + +.holographic-panel { + margin-block: 2rem; + padding: 2rem; + border-radius: var(--radius, 20px); + position: relative; + overflow: hidden; + min-height: 350px; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.holo-content { + width: 100%; + max-width: 400px; + text-align: center; + position: relative; + z-index: 1; +} + +.holo-svg { + width: 100%; + max-width: 300px; + height: auto; + display: block; + margin: 0 auto; + filter: drop-shadow(0 0 10px currentColor); +} + +.holo-label { + font-family: var(--font-mono, monospace); + font-size: 1rem; + font-weight: 700; + letter-spacing: 0.15em; + margin-top: 1.5rem; + text-transform: uppercase; + opacity: 0.9; +} + +/* ===== LCARS Theme (Star Trek) ===== */ +.theme-lcars .holographic-panel { + background: + linear-gradient(180deg, + color-mix(in oklab, #0b0e12 95%, #ffa000) 0%, + color-mix(in oklab, #0b0e12 98%, #ffa000) 100%); + border: 3px solid color-mix(in oklab, #ffa000 70%, transparent); + box-shadow: + inset 0 0 0 1px rgba(255, 160, 0, 0.1), + 0 0 40px rgba(255, 160, 0, 0.15); +} + +.holo-lcars { + color: #ffa000; +} + +.holo-lcars .holo-label { + color: #ff9a00; + text-shadow: 0 0 8px rgba(255, 160, 0, 0.6); +} + +/* LCARS Radar sweep animation */ +@keyframes lcars-radar-sweep { + 0% { + transform: rotate(0deg); + transform-origin: 150px 150px; + } + 100% { + transform: rotate(360deg); + transform-origin: 150px 150px; + } +} + +.theme-lcars .radar-sweep { + animation: lcars-radar-sweep 4s linear infinite; +} + +/* LCARS blip pulse */ +@keyframes lcars-blip-pulse { + 0%, 100% { + opacity: 0.3; + r: 3; + } + 50% { + opacity: 1; + r: 4; + } +} + +.theme-lcars .blip { + animation: lcars-blip-pulse 2s ease-in-out infinite; +} + +.theme-lcars .blip-1 { + animation-delay: 0s; +} + +.theme-lcars .blip-2 { + animation-delay: 0.7s; +} + +.theme-lcars .blip-3 { + animation-delay: 1.4s; +} + +/* ===== Star Wars Theme (Holo) ===== */ +.theme-holo .holographic-panel { + background: + linear-gradient(180deg, + color-mix(in oklab, #0b0e12 95%, #2ee6ff) 0%, + color-mix(in oklab, #0b0e12 98%, #2ee6ff) 100%); + border: 3px solid color-mix(in oklab, #2ee6ff 60%, transparent); + box-shadow: + inset 0 0 0 1px rgba(46, 230, 255, 0.1), + 0 0 40px rgba(46, 230, 255, 0.2); +} + +.holo-starwars { + color: #2ee6ff; +} + +.holo-starwars .holo-label { + color: #90f0ff; + text-shadow: 0 0 8px rgba(46, 230, 255, 0.8); +} + +/* Star Wars hovering rings animation */ +@keyframes holo-ring-float { + 0%, 100% { + transform: translateY(0) scale(1); + opacity: 0.6; + } + 50% { + transform: translateY(-10px) scale(1.05); + opacity: 0.9; + } +} + +.theme-holo .holo-ring { + animation: holo-ring-float 3s ease-in-out infinite; +} + +.theme-holo .ring-1 { + animation-delay: 0s; +} + +.theme-holo .ring-2 { + animation-delay: 0.3s; +} + +.theme-holo .ring-3 { + animation-delay: 0.6s; +} + +/* Star Wars glow pulse */ +@keyframes holo-glow-pulse { + 0%, 100% { + opacity: 0.2; + } + 50% { + opacity: 0.5; + } +} + +.theme-holo .holo-svg circle[r="15"] { + animation: holo-glow-pulse 2s ease-in-out infinite; +} + +/* ===== Cyberpunk Theme ===== */ +.theme-cyber .holographic-panel { + background: + linear-gradient(180deg, + color-mix(in oklab, #120012 95%, #ff2ec0) 0%, + color-mix(in oklab, #120012 98%, #ff2ec0) 100%); + border: 3px solid color-mix(in oklab, #ff2ec0 70%, transparent); + box-shadow: + inset 0 0 0 1px rgba(255, 46, 192, 0.1), + 0 0 40px rgba(255, 46, 192, 0.2); + position: relative; +} + +.holo-cyber { + color: #ff2ec0; +} + +.holo-cyber .holo-label { + color: #25f3ff; + text-shadow: + 0 0 8px rgba(37, 243, 255, 0.8), + 0 0 16px rgba(255, 46, 192, 0.4); + position: relative; +} + +/* Cyberpunk glitch effect on label */ +@keyframes cyber-glitch { + 0% { + transform: translateX(0); + opacity: 1; + } + 10% { + transform: translateX(-2px); + } + 20% { + transform: translateX(2px); + } + 30% { + transform: translateX(-2px); + } + 40% { + transform: translateX(2px); + } + 50% { + transform: translateX(0); + } + 100% { + transform: translateX(0); + opacity: 1; + } +} + +.theme-cyber .holo-label[data-glitch]::before { + content: attr(data-glitch); + position: absolute; + top: 0; + left: 0; + right: 0; + color: #ff2ec0; + mix-blend-mode: screen; + animation: cyber-glitch 3s linear infinite; + clip-path: inset(0 0 50% 0); +} + +/* Cyberpunk scan line animation */ +@keyframes cyber-scan { + 0% { + transform: translateY(0); + } + 100% { + transform: translateY(300px); + } +} + +.theme-cyber .scan-line { + animation: cyber-scan 3s linear infinite; +} + +/* Cyberpunk grid flicker */ +@keyframes cyber-grid-flicker { + 0%, 100% { + opacity: 0.4; + } + 50% { + opacity: 0.2; + } + 75% { + opacity: 0.5; + } +} + +.theme-cyber .grid-lines { + animation: cyber-grid-flicker 2s ease-in-out infinite; +} + +/* ===== Reduced Motion Support ===== */ +@media (prefers-reduced-motion: reduce) { + .holographic-panel[data-reduced-motion="true"] * { + animation: none !important; + transition: none !important; + } + + .holographic-panel[data-reduced-motion="true"] .radar-sweep, + .holographic-panel[data-reduced-motion="true"] .holo-ring, + .holographic-panel[data-reduced-motion="true"] .scan-line, + .holographic-panel[data-reduced-motion="true"] .blip { + animation: none !important; + } + + .holographic-panel[data-reduced-motion="true"] .holo-label::before { + animation: none !important; + } +} + +/* ===== Responsive Design ===== */ +@media (max-width: 768px) { + .holographic-panel { + padding: 1.5rem; + min-height: 300px; + } + + .holo-svg { + max-width: 250px; + } + + .holo-label { + font-size: 0.875rem; + margin-top: 1rem; + } +} + +@media (max-width: 480px) { + .holographic-panel { + padding: 1rem; + min-height: 250px; + } + + .holo-svg { + max-width: 200px; + } + + .holo-label { + font-size: 0.75rem; + } +}