|
<!DOCTYPE html> |
|
<html lang="fa" dir="rtl"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
|
<title>هوشان - نسخه اصلاح شده نهایی</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<style> |
|
@custom-variant dark (&:is(.dark *)); |
|
@theme inline { |
|
--color-background: var(--background); |
|
--color-foreground: var(--foreground); |
|
--radius-lg: var(--radius); |
|
--popover: oklch(1 0 0); |
|
--popover-foreground: oklch(0.145 0 0); |
|
--border: oklch(0.922 0 0); |
|
--card: oklch(1 0 0); |
|
--card-foreground: oklch(0.145 0 0); |
|
} |
|
:root { |
|
--radius: 0.625rem; |
|
--background: oklch(1 0 0); |
|
--foreground: oklch(0.145 0 0); |
|
} |
|
.dark { |
|
--background: oklch(0.145 0 0); |
|
--foreground: oklch(0.985 0 0); |
|
--popover: oklch(0.205 0 0); |
|
--popover-foreground: oklch(0.985 0 0); |
|
--border: oklch(1 0 0 / 10%); |
|
--card: oklch(0.205 0 0); |
|
--card-foreground: oklch(0.985 0 0); |
|
} |
|
@layer base { |
|
* { @apply border-border outline-ring/50; } |
|
body { @apply bg-background text-foreground; overflow-x: hidden; margin:0; padding:0; font-family: sans-serif; } |
|
} |
|
|
|
|
|
.notification-popover-wrapper { |
|
position: fixed; top: 1rem; left: 50%; transform: translateX(-50%); |
|
z-index: 100; width: calc(100% - 2rem); max-width: 28rem; |
|
display: flex; justify-content: center; pointer-events: none; |
|
} |
|
.popover-content { |
|
width: 100%; border-radius: var(--radius-md, 0.5rem); border-width: 1px; |
|
border-color: var(--border); background-color: var(--popover); |
|
color: var(--popover-foreground); |
|
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); |
|
outline: none; transition: opacity 0.3s ease-out, transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55); |
|
opacity: 0; transform: translateY(-30px) scale(0.95); |
|
pointer-events: none; |
|
visibility: hidden; |
|
} |
|
.popover-content.open { |
|
opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; visibility: visible; |
|
} |
|
.notification-popover-text-content { |
|
background-color: #eff6ff; |
|
font-size: 0.875rem; line-height: 1.5rem; direction: rtl; |
|
padding: 1rem; border-radius: inherit; |
|
} |
|
|
|
|
|
.header-controls { |
|
display: flex; padding: 1rem; justify-content: space-between; |
|
align-items: center; width: 100%; position: absolute; |
|
top: 0; left: 0; z-index: 10; |
|
} |
|
.header-button { |
|
display: flex; align-items: center; justify-content: center; |
|
padding: 0.5rem; border-radius: var(--radius-lg, 0.625rem); |
|
background-color: #e5e7eb; |
|
cursor: pointer; |
|
color: var(--foreground); |
|
} |
|
.header-button svg { opacity: 0.6; } |
|
|
|
@keyframes ping { 75%, 100% { transform: scale(2); opacity: 0; } } |
|
.animate-ping { animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; } |
|
|
|
#large-logo-container.flex { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; position: absolute; top: 0; left: 0; } |
|
#small-logo-container.flex { display: flex; align-items: center; justify-content: center; } |
|
|
|
.footer-controls { |
|
width: 100%; display: flex; gap: 1rem; position: absolute; |
|
bottom: 0; padding: 2rem 3rem; align-items: center; |
|
} |
|
.footer-controls.layout-default { justify-content: space-between; } |
|
.footer-controls.layout-with-small-logo { justify-content: space-around; } |
|
|
|
.control-button-wrapper { |
|
position: relative; display: flex; align-items: center; justify-content: center; |
|
} |
|
.camera-switch-icon { |
|
position: absolute; |
|
bottom: calc(100% + 10px); |
|
left: 50%; |
|
transform: translateX(-50%) translateY(10px) scale(0.7); |
|
background-color: var(--card); |
|
color: var(--card-foreground); |
|
border-radius: 50%; |
|
padding: 10px; |
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1), 0 1px 3px rgba(0,0,0,0.08); |
|
cursor: pointer; |
|
z-index: 5; |
|
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
|
opacity: 0; |
|
pointer-events: none; |
|
} |
|
.camera-switch-icon.visible { |
|
opacity: 1; |
|
transform: translateX(-50%) translateY(0) scale(1); |
|
pointer-events: auto; |
|
} |
|
.camera-switch-icon svg { |
|
width: 24px; |
|
height: 24px; |
|
} |
|
|
|
.control-button { |
|
height: 80px; width: 80px; border-radius: 9999px; |
|
padding: 0; display: flex; align-items: center; justify-content: center; |
|
border-width: 1px; |
|
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06); |
|
cursor: pointer; |
|
} |
|
.cam-button-color { background-color: #E0ECFF; } |
|
.mic-button-color { background-color: #fecdd3; } |
|
|
|
</style> |
|
<script> |
|
tailwind.config = { |
|
darkMode: 'class', |
|
theme: { |
|
extend: { |
|
colors: { }, borderRadius: { }, inset: { }, |
|
keyframes: { |
|
'popover-drop-in': { |
|
'0%': { opacity: '0', transform: 'translateY(-30px) scale(0.95)' }, |
|
'70%': { opacity: '1', transform: 'translateY(5px) scale(1.02)' }, |
|
'100%': { opacity: '1', transform: 'translateY(0) scale(1)' }, |
|
}, |
|
'popover-lift-out': { |
|
'0%': { opacity: '1', transform: 'translateY(0) scale(1)' }, |
|
'100%': { opacity: '0', transform: 'translateY(-30px) scale(0.95)' }, |
|
} |
|
}, |
|
animation: { |
|
'popover-open-top-center': 'popover-drop-in 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55) forwards', |
|
'popover-close-top-center': 'popover-lift-out 0.3s ease-in forwards', |
|
} |
|
} |
|
} |
|
} |
|
</script> |
|
</head> |
|
<body class="antialiased bg-slate-50"> |
|
|
|
<div class="w-full flex flex-col items-center justify-center min-h-[90dvh] md:min-h-screen"> |
|
<div class="max-w-3xl w-full flex flex-col items-center justify-center h-full relative"> |
|
|
|
<div class="header-controls"> |
|
<div id="notification-trigger-container"> |
|
<button id="notification-button" aria-label="Notifications" class="header-button"> |
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg> |
|
</button> |
|
</div> |
|
<div> |
|
<div class="header-button" onclick="alert('Back clicked')"> |
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"></path></svg> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div id="notification-popover-wrapper" class="notification-popover-wrapper"> |
|
<div id="notification-popover" class="popover-content"> |
|
<div class="notification-popover-text-content"> |
|
مدلهای هوش مصنوعی میتوانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از وارد کردن اطلاعات حساس بپرهیزید. |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="w-full flex flex-col items-center justify-center h-[90dvh] bg-slate-50 top-0 left-0 relative"> |
|
<video id="video-feed" autoplay playsinline class="absolute top-0 left-0 w-full h-full object-cover scale-x-[-1] hidden"></video> |
|
<div id="large-logo-container" class="hidden"></div> |
|
|
|
<div id="footer-controls" class="footer-controls layout-default"> |
|
|
|
<div class="control-button-wrapper"> |
|
<div id="cam-switch-icon" class="camera-switch-icon"> |
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> |
|
<path d="M11 19H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"/> |
|
<path d="M13 5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"/> |
|
<line x1="12" x2="12" y1="1" y2="23"/> |
|
<polyline points="8 13 12 17 16 13"/> |
|
<polyline points="16 11 12 7 8 11"/> |
|
</svg> |
|
</div> |
|
<div id="cam-button" class="control-button cam-button-color"></div> |
|
</div> |
|
|
|
<div id="small-logo-container" class="hidden"></div> |
|
|
|
|
|
<div id="mic-button" class="control-button mic-button-color"></div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div style="display: none;"> |
|
<svg id="svg-pauseIcon">...</svg><svg id="svg-microphoneIcon">...</svg><svg id="svg-cameraIcon">...</svg><svg id="svg-stopCamIcon">...</svg><svg id="svg-humanIcon">...</svg> |
|
</div> |
|
|
|
<script> |
|
const videoFeed = document.getElementById('video-feed'); |
|
const largeLogoContainer = document.getElementById('large-logo-container'); |
|
const smallLogoContainer = document.getElementById('small-logo-container'); |
|
const micButton = document.getElementById('mic-button'); |
|
const camButton = document.getElementById('cam-button'); |
|
const camSwitchIcon = document.getElementById('cam-switch-icon'); |
|
const footerControls = document.getElementById('footer-controls'); |
|
const notificationButton = document.getElementById('notification-button'); |
|
const notificationPopover = document.getElementById('notification-popover'); |
|
|
|
const pauseIconSVG = document.getElementById('svg-pauseIcon').cloneNode(true); |
|
const microphoneIconSVG = document.getElementById('svg-microphoneIcon').cloneNode(true); |
|
const cameraIconSVG = document.getElementById('svg-cameraIcon').cloneNode(true); |
|
const stopCamIconSVG = document.getElementById('svg-stopCamIcon').cloneNode(true); |
|
|
|
let isMicActive = false; |
|
let isCamActive = false; |
|
let isNotificationOpen = false; |
|
|
|
function createLogoHTML(isMini, isHumanActive) { } |
|
|
|
function updateUI() { |
|
videoFeed.classList.toggle('hidden', !isCamActive); |
|
largeLogoContainer.classList.toggle('hidden', !(!isCamActive && isMicActive)); |
|
largeLogoContainer.classList.toggle('flex', !isCamActive && isMicActive); |
|
if(!isCamActive && isMicActive) largeLogoContainer.innerHTML = createLogoHTML(false, true); |
|
|
|
smallLogoContainer.classList.toggle('hidden', !isCamActive); |
|
smallLogoContainer.classList.toggle('flex', isCamActive); |
|
if(isCamActive) smallLogoContainer.innerHTML = createLogoHTML(true, true); |
|
|
|
footerControls.classList.toggle('layout-default', !isCamActive); |
|
footerControls.classList.toggle('layout-with-small-logo', isCamActive); |
|
|
|
camSwitchIcon.classList.toggle('visible', isCamActive); |
|
|
|
camButton.innerHTML = ''; |
|
camButton.appendChild(isCamActive ? stopCamIconSVG.cloneNode(true) : cameraIconSVG.cloneNode(true)); |
|
micButton.innerHTML = ''; |
|
micButton.appendChild(isMicActive ? pauseIconSVG.cloneNode(true) : microphoneIconSVG.cloneNode(true)); |
|
|
|
|
|
if (isNotificationOpen) { |
|
notificationPopover.style.display = 'block'; |
|
requestAnimationFrame(() => { |
|
notificationPopover.classList.remove('animate-popover-close-top-center'); |
|
notificationPopover.classList.add('open', 'animate-popover-open-top-center'); |
|
}); |
|
} else { |
|
if (notificationPopover.classList.contains('open')) { |
|
notificationPopover.classList.remove('animate-popover-open-top-center'); |
|
notificationPopover.classList.add('animate-popover-close-top-center'); |
|
setTimeout(() => { |
|
if (!isNotificationOpen) { |
|
notificationPopover.classList.remove('open'); |
|
notificationPopover.style.display = 'none'; |
|
} |
|
}, 300); |
|
} else { |
|
notificationPopover.style.display = 'none'; |
|
notificationPopover.classList.remove('open'); |
|
} |
|
} |
|
} |
|
|
|
notificationButton.addEventListener('click', (event) => { |
|
event.stopPropagation(); |
|
isNotificationOpen = !isNotificationOpen; |
|
updateUI(); |
|
}); |
|
|
|
document.addEventListener('click', (event) => { |
|
if (isNotificationOpen && !notificationPopover.contains(event.target) && event.target !== notificationButton) { |
|
isNotificationOpen = false; |
|
updateUI(); |
|
} |
|
}); |
|
|
|
camButton.addEventListener('click', () => { isCamActive = !isCamActive; updateUI(); }); |
|
micButton.addEventListener('click', () => { isMicActive = !isMicActive; updateUI(); }); |
|
camSwitchIcon.addEventListener('click', (e) => { |
|
e.stopPropagation(); alert('Camera switch clicked!'); |
|
}); |
|
|
|
|
|
notificationPopover.style.opacity = '0'; |
|
notificationPopover.style.transform = 'translateY(-30px) scale(0.95)'; |
|
notificationPopover.style.visibility = 'hidden'; |
|
notificationPopover.style.display = 'none'; |
|
updateUI(); |
|
</script> |
|
</body> |
|
</html> |