llm / index.html
2ch's picture
Update index.html
e6da5ce verified
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Site Switcher</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<style>
:root {
--bg: #0a0a0b;
--fg: #f0f0f2;
--muted: #6b6b70;
--accent: #00d4aa;
--accent-dim: rgba(0, 212, 170, 0.15);
--card: #161618;
--card-hover: #1e1e21;
--border: #2a2a2d;
--glow: rgba(0, 212, 170, 0.4);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Space Grotesk', sans-serif;
background: var(--bg);
overflow: hidden;
height: 100vh;
width: 100vw;
}
/* Full-screen iframe */
.site-frame {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
z-index: 1;
background: var(--bg);
}
/* Floating switcher */
.switcher {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
}
/* Menu dropdown */
.switcher-menu {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 12px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
margin-bottom: 12px;
opacity: 0;
visibility: hidden;
transform: translateY(10px) scale(0.95);
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
min-width: 200px;
}
.switcher-menu.active {
opacity: 1;
visibility: visible;
transform: translateY(0) scale(1);
}
.menu-item {
width: 100%;
padding: 12px 16px;
background: transparent;
border: 1px solid transparent;
border-radius: 10px;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
gap: 12px;
color: var(--fg);
font-size: 14px;
font-weight: 500;
text-decoration: none;
}
.menu-item:hover {
background: var(--card-hover);
border-color: var(--border);
}
.menu-item.active {
background: var(--accent-dim);
border-color: var(--accent);
color: var(--accent);
}
.menu-item .status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--muted);
transition: all 0.15s ease;
flex-shrink: 0;
}
.menu-item.active .status-dot {
background: var(--accent);
box-shadow: 0 0 8px var(--glow);
}
.menu-item:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
/* Toggle button */
.toggle-btn {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 20px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 100px;
cursor: pointer;
transition: all 0.2s ease;
color: var(--fg);
font-size: 13px;
font-weight: 500;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
}
.toggle-btn:hover {
background: var(--card-hover);
border-color: var(--accent);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5), 0 0 20px var(--glow);
}
.toggle-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.toggle-btn .chevron {
width: 16px;
height: 16px;
transition: transform 0.25s ease;
}
.toggle-btn.active .chevron {
transform: rotate(180deg);
}
.current-domain {
color: var(--accent);
font-weight: 600;
}
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.switcher {
animation: fadeIn 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.3s both;
}
@media (prefers-reduced-motion: reduce) {
.switcher,
.switcher-menu,
.menu-item,
.toggle-btn {
animation: none;
transition: none;
}
}
/* Mobile adjustments */
@media (max-width: 480px) {
.switcher {
bottom: 16px;
}
.toggle-btn {
padding: 10px 16px;
font-size: 12px;
}
.switcher-menu {
min-width: 180px;
}
}
</style>
</head>
<body>
<!-- Main iframe -->
<iframe id="siteFrame" class="site-frame" title="Embedded website"></iframe>
<!-- Floating switcher -->
<div class="switcher">
<div id="menu" class="switcher-menu" role="menu" aria-label="Выбор сайта">
<!-- Items injected by JS -->
</div>
<button id="toggleBtn" class="toggle-btn" aria-expanded="false" aria-controls="menu">
<span class="current-domain" id="currentDomain"></span>
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
</div>
<script>
// ============================================
// Список сайтов - редактируйте здесь
// ============================================
const SITES = [
{ url: 'https://z.ai/', name: null },
{ url: 'https://chat.qwen.ai/', name: null },
{ url: 'https://chat.deepseek.com/', name: null },
// Добавляйте новые сайты по аналогии:
// { url: 'https://example.com', name: 'Кастомное имя (опционально)' },
];
// ============================================
// Extract domain from URL
function extractDomain(url) {
try {
const urlObj = new URL(url);
return urlObj.hostname.replace('www.', '');
} catch (e) {
return url;
}
}
// State
let currentSiteIndex = 0;
let isMenuOpen = false;
// DOM elements
const siteFrame = document.getElementById('siteFrame');
const menu = document.getElementById('menu');
const toggleBtn = document.getElementById('toggleBtn');
const currentDomainEl = document.getElementById('currentDomain');
// Render menu items
function renderMenu() {
menu.innerHTML = '';
SITES.forEach((site, index) => {
const domain = extractDomain(site.url);
const displayName = site.name || domain;
const item = document.createElement('button');
item.className = `menu-item ${index === currentSiteIndex ? 'active' : ''}`;
item.setAttribute('role', 'menuitem');
item.setAttribute('tabindex', isMenuOpen ? '0' : '-1');
item.innerHTML = `
<span class="status-dot"></span>
<span>${displayName}</span>
`;
item.addEventListener('click', () => selectSite(index));
menu.appendChild(item);
});
}
// Select site
function selectSite(index) {
currentSiteIndex = index;
const site = SITES[index];
const domain = extractDomain(site.url);
siteFrame.src = site.url;
currentDomainEl.textContent = domain;
renderMenu();
closeMenu();
}
// Toggle menu
function toggleMenu() {
isMenuOpen ? closeMenu() : openMenu();
}
function openMenu() {
isMenuOpen = true;
menu.classList.add('active');
toggleBtn.classList.add('active');
toggleBtn.setAttribute('aria-expanded', 'true');
// Focus first item
const firstItem = menu.querySelector('.menu-item');
if (firstItem) firstItem.focus();
}
function closeMenu() {
isMenuOpen = false;
menu.classList.remove('active');
toggleBtn.classList.remove('active');
toggleBtn.setAttribute('aria-expanded', 'false');
}
// Event listeners
toggleBtn.addEventListener('click', toggleMenu);
// Close on outside click
document.addEventListener('click', (e) => {
if (!e.target.closest('.switcher') && isMenuOpen) {
closeMenu();
}
});
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (!isMenuOpen) return;
const items = menu.querySelectorAll('.menu-item');
const currentIndex = Array.from(items).findIndex(item => item === document.activeElement);
if (e.key === 'Escape') {
closeMenu();
toggleBtn.focus();
} else if (e.key === 'ArrowDown') {
e.preventDefault();
const nextIndex = (currentIndex + 1) % items.length;
items[nextIndex].focus();
} else if (e.key === 'ArrowUp') {
e.preventDefault();
const prevIndex = (currentIndex - 1 + items.length) % items.length;
items[prevIndex].focus();
} else if (e.key === 'Enter' && currentIndex >= 0) {
selectSite(currentIndex);
}
});
// Initialize
renderMenu();
selectSite(0);
</script>
</body>
</html>