|
|
<!DOCTYPE html> |
|
|
<html lang="fa" dir="rtl"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>تولید صدای هوشمند با هوش مصنوعی | AI Sada - تبدیل متن به گفتار فارسی</title> |
|
|
<meta name="description" content="با AI Sada، متن فارسی خود را به صدایی طبیعی و با کیفیت استودیویی تبدیل کنید. از میان دهها گوینده حرفهای انتخاب کرده یا صدای خود را شبیهسازی کنید. سریع، آسان و قدرتمند."> |
|
|
<link rel="canonical" href="https://www.aisada.ir/"> |
|
|
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
|
|
|
|
|
|
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" async defer></script> |
|
|
|
|
|
<style> |
|
|
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&display=swap'); |
|
|
|
|
|
:root { |
|
|
--app-font: 'Vazirmatn', sans-serif; --app-bg: #F8F9FC; --panel-bg: #FFFFFF; |
|
|
--panel-border: #EAEFF7; --input-bg: #F6F8FB; --input-border: #E1E7EF; |
|
|
--text-primary: #1A202C; --text-secondary: #626F86; --text-tertiary: #8A94A6; |
|
|
--accent-primary: #4A6CFA; --accent-primary-hover: #3553D6; --accent-primary-glow: rgba(74, 108, 250, 0.25); |
|
|
--accent-secondary: #0FD4A8; --accent-secondary-hover: #0DA986; --accent-secondary-glow: rgba(15, 212, 168, 0.2); |
|
|
--accent-premium: #FFC107; --accent-premium-glow: rgba(255, 193, 7, 0.3); |
|
|
--shadow-sm: 0 1px 2px 0 rgba(26, 32, 44, 0.03); --shadow-md: 0 4px 6px -1px rgba(26, 32, 44, 0.05), 0 2px 4px -2px rgba(26, 32, 44, 0.04); |
|
|
--shadow-lg: 0 10px 15px -3px rgba(26, 32, 44, 0.06), 0 4px 6px -4px rgba(26, 32, 44, 0.05); |
|
|
--shadow-xl: 0 20px 25px -5px rgba(26, 32, 44, 0.07), 0 8px 10px -6px rgba(26, 32, 44, 0.05); |
|
|
--radius-card: 24px; --radius-btn: 14px; --radius-input: 12px; |
|
|
--transition-fast: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
--transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
--transition-bounce: all 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55); |
|
|
|
|
|
--card-bg: #FFFFFF; |
|
|
--danger: #E53E3E; |
|
|
} |
|
|
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } } |
|
|
@keyframes modalZoomIn { from { opacity: 0; transform: scale(0.9) translateY(20px); } to { opacity: 1; transform: scale(1) translateY(0); } } |
|
|
@keyframes scaleIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } } |
|
|
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } |
|
|
@keyframes rotate-loader-orbital { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } |
|
|
@keyframes orbit-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } |
|
|
@keyframes satellite-pulse-1 { from { transform: scale(0.7) translateX(-50%); opacity: 0.6; } to { transform: scale(1.1) translateX(-50%); opacity: 1; } } |
|
|
@keyframes satellite-pulse-2 { from { transform: scale(0.7) translateY(-50%); opacity: 0.6; } to { transform: scale(1.1) translateY(-50%); opacity: 1; } } |
|
|
@keyframes satellite-pulse-3 { from { transform: scale(0.7) translateX(50%); opacity: 0.6; } to { transform: scale(1.1) translateX(50%); opacity: 1; } } |
|
|
@keyframes shimmer { 100% { transform: translateX(100%); } } |
|
|
@keyframes pulse-soft { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } |
|
|
|
|
|
html { scroll-behavior: smooth; } |
|
|
body { |
|
|
font-family: var(--app-font); direction: rtl; background-color: var(--app-bg); |
|
|
color: var(--text-primary); font-size: 16px; line-height: 1.8; margin: 0; |
|
|
padding: 0; min-height: 100vh; -webkit-font-smoothing: antialiased; |
|
|
-moz-osx-font-smoothing: grayscale; |
|
|
} |
|
|
.page-wrapper { max-width: 820px; width: 92%; margin: 0 auto; padding: 2.5rem 0; } |
|
|
.app-container { max-width: 820px; width: 100%; margin: 0 auto; margin-bottom: 5rem; } |
|
|
.app-header { padding: 0.5rem 0 2.5rem 0; text-align: center; margin-bottom: 1.5rem; animation: fadeIn 0.8s 0.2s ease-out backwards; } |
|
|
.app-header h1 { font-size: 2.5em; font-weight: 900; margin: 0 0 0.8rem 0; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; letter-spacing: -1px; } |
|
|
.app-header p { font-size: 1.05em; color: var(--text-secondary); margin-top: 0; opacity: 0.9; font-weight: 400; line-height: 1.7; } |
|
|
.main-content { position:relative; padding: 3rem; background-color: var(--panel-bg); border-radius: var(--radius-card); box-shadow: var(--shadow-xl); border: 1px solid var(--panel-border); animation: fadeIn 0.8s 0.4s ease-out backwards; } |
|
|
.form-group { margin-bottom: 2.2rem; } |
|
|
label { display: block; font-weight: 700; color: var(--text-primary); font-size: 1.1em; margin-bottom: 1rem; } |
|
|
textarea, input[type="text"], input[type="email"] { width: 100%; padding: 1rem 1.2rem; border-radius: var(--radius-input); border: 1px solid var(--input-border); background-color: var(--input-bg); color: var(--text-primary); box-shadow: var(--shadow-sm) inset; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; transition: var(--transition-smooth); } |
|
|
textarea:focus, input[type="text"]:focus, input[type="email"]:focus { outline: none; border-color: var(--accent-primary); box-shadow: 0 0 0 3px var(--accent-primary-glow), var(--shadow-sm) inset; background-color: var(--panel-bg); } |
|
|
textarea { min-height: 120px; resize: vertical; } |
|
|
.slider-container { display: flex; align-items: center; gap: 1.5rem; } |
|
|
input[type="range"] { flex-grow: 1; -webkit-appearance: none; appearance: none; width: 100%; height: 6px; background: var(--input-border); border-radius: 3px; outline: none; cursor: pointer; } |
|
|
input[type="range"]::-webkit-slider-runnable-track { background: linear-gradient(to right, var(--accent-secondary) 0%, var(--accent-primary) 100%); height: 6px; border-radius: 3px; } |
|
|
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; background: #fff; border-radius: 50%; cursor: pointer; border: 4px solid var(--accent-primary); box-shadow: var(--shadow-md); margin-top: -9px; transition: var(--transition-fast); } |
|
|
input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.15); box-shadow: 0 0 0 8px var(--accent-primary-glow); } |
|
|
.temperature-value { font-weight: 700; background-color: var(--input-bg); padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid var(--input-border); min-width: 45px; text-align: center; color: var(--accent-primary); font-size: 1em; box-shadow: var(--shadow-sm); } |
|
|
.generate-btn { display: flex; align-items: center; justify-content: center; gap: 10px; width: 100%; padding: 1.1rem 1.5rem; font-size: 1.25em; font-weight: 800; background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%); color: #fff; border: none; border-radius: var(--radius-btn); cursor: pointer; transition: all 0.3s ease; box-shadow: 0 6px 12px -3px var(--accent-primary-glow), 0 6px 12px -3px var(--accent-secondary-glow); position: relative; overflow: hidden; letter-spacing: 0.5px; } |
|
|
.generate-btn:hover:not(:disabled) { transform: translateY(-5px) scale(1.02); box-shadow: 0 8px 20px -4px var(--accent-primary-glow), 0 8px 20px -4px var(--accent-secondary-glow); } |
|
|
.generate-btn:disabled { background: var(--text-tertiary); cursor: not-allowed; box-shadow: none; color: rgba(255,255,255,0.7); } |
|
|
.generate-btn .spinner { width: 20px; height: 20px; border: 3px solid rgba(255, 255, 255, 0.4); border-top-color: #fff; border-radius: 50%; animation: spin 0.8s linear infinite; display: none;} |
|
|
.output-section { margin-top: 3rem; display: flex; align-items: center; justify-content: center; flex-direction: column; min-height: 220px; position: relative; box-sizing: border-box; padding: 2rem; background-color: var(--input-bg); border-radius: var(--radius-card); border: 2px dashed var(--input-border); box-shadow: var(--shadow-sm) inset; transition: var(--transition-smooth); } |
|
|
.output-section.has-content { background-color: var(--panel-bg); border: 1px solid var(--panel-border); box-shadow: var(--shadow-lg); padding: 0; min-height: auto; } |
|
|
.status-message { font-weight: 500; color: var(--text-secondary); text-align: center; font-size: 1.1em; } |
|
|
.status-message.error { color: #e53e3e; background-color: #fed7d7; padding: 1rem; border-radius: var(--radius-input); border: 1px solid #f56565; } |
|
|
.loading-animation-wrapper { display: none; flex-direction: column; align-items: center; justify-content: center; gap: 1.8rem; width: 100%; } |
|
|
.orbital-loader { width: 110px; height: 110px; position: relative; animation: rotate-loader-orbital 10s linear infinite; } |
|
|
.orbit { position: absolute; top: 50%; left: 50%; border: 2px dashed rgba(74, 108, 250, 0.35); border-radius: 50%; transform-origin: center center; } |
|
|
.orbit:nth-child(1) { width: 35px; height: 35px; margin: -17.5px 0 0 -17.5px; animation: orbit-spin 2.8s linear infinite reverse; } |
|
|
.orbit:nth-child(2) { width: 65px; height: 65px; margin: -32.5px 0 0 -32.5px; animation: orbit-spin 3.8s linear infinite; } |
|
|
.orbit:nth-child(3) { width: 95px; height: 95px; margin: -47.5px 0 0 -47.5px; animation: orbit-spin 4.8s linear infinite reverse; } |
|
|
.orbit .satellite { position: absolute; width: 10px; height: 10px; border-radius: 50%; background-color: var(--accent-primary); box-shadow: 0 0 8px var(--accent-primary), 0 0 12px var(--accent-secondary); } |
|
|
.orbit:nth-child(1) .satellite { top: -5px; left: 50%; animation: satellite-pulse-1 1.4s ease-in-out infinite alternate; } |
|
|
.orbit:nth-child(2) .satellite { top: 50%; left: -5px; background-color: var(--accent-secondary); animation: satellite-pulse-2 1.4s 0.2s ease-in-out infinite alternate; } |
|
|
.orbit:nth-child(3) .satellite { bottom: -5px; right: 50%; animation: satellite-pulse-3 1.4s 0.4s ease-in-out infinite alternate;} |
|
|
.loading-text { font-size: 1.2em; font-weight: 700; color: var(--text-primary); text-align: center; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } |
|
|
.audio-player-content { display: none; width: 100%; animation: fadeIn 0.5s ease-out; } |
|
|
|
|
|
|
|
|
.simple-player-container { display: flex; flex-direction: column; align-items: center; gap: 1rem; width: 100%; padding: 1.5rem; box-sizing: border-box; background-color: var(--input-bg); border-radius: var(--radius-input); border: 1px solid var(--panel-border); margin-top: 10px;} |
|
|
.play-controls { display: flex; align-items: center; gap: 1rem; width: 100%; } |
|
|
.play-pause-btn-simple { background: linear-gradient(135deg, var(--accent-secondary), var(--accent-primary)); color: white; border: none; border-radius: 50%; width: 55px; height: 55px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(74, 108, 250, 0.4); flex-shrink: 0; } |
|
|
.play-pause-btn-simple:hover { transform: scale(1.1); box-shadow: 0 6px 20px rgba(74, 108, 250, 0.5); } |
|
|
.play-pause-btn-simple svg { width: 28px; height: 28px; fill: currentColor; } |
|
|
.play-pause-btn-simple .play-icon { margin-right: -3px; } |
|
|
.progress-wrapper { display: flex; align-items: center; gap: 1rem; width: 100%; } |
|
|
.time-display { font-size: 0.9em; color: var(--text-secondary); font-variant-numeric: tabular-nums; min-width: 40px; text-align: center; } |
|
|
.audio-progress-bar { -webkit-appearance: none; appearance: none; width: 100%; height: 8px; background: #e2e8f0; border-radius: 5px; outline: none; cursor: pointer; transition: all 0.2s ease; flex-grow: 1;} |
|
|
.audio-progress-bar::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 20px; height: 20px; background: var(--accent-primary); border-radius: 50%; cursor: pointer; border: 3px solid white; box-shadow: var(--shadow-md); } |
|
|
.audio-progress-bar::-moz-range-thumb { width: 20px; height: 20px; background: var(--accent-primary); border-radius: 50%; cursor: pointer; border: 3px solid white; box-shadow: var(--shadow-md); } |
|
|
.audio-download-btn-new, .audio-download-btn-new:visited { display: inline-flex; align-items: center; justify-content: center; gap: 10px; width: 100%; max-width: 320px; margin-top: 0.5rem; padding: 0.9rem 1.5rem; font-size: 1.1em; font-weight: 700; background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%); color: #fff !important; text-decoration: none !important; border: none; border-radius: var(--radius-btn); cursor: pointer; transition: all 0.3s ease; box-shadow: 0 5px 15px -3px var(--accent-primary-glow), 0 5px 15px -3px var(--accent-secondary-glow); } |
|
|
.audio-download-btn-new:hover { transform: translateY(-3px); box-shadow: 0 8px 20px -4px var(--accent-primary-glow), 0 8px 20px -4px var(--accent-secondary-glow); } |
|
|
.audio-download-btn-new svg { width: 20px; height: 20px; fill: currentColor; } |
|
|
|
|
|
|
|
|
.simple-player-container.compact-player { |
|
|
padding: 0.8rem; |
|
|
gap: 0.5rem; |
|
|
background: #f8f9fa; |
|
|
border: 1px solid #e9ecef; |
|
|
} |
|
|
.simple-player-container.compact-player .play-pause-btn-simple { |
|
|
width: 40px; |
|
|
height: 40px; |
|
|
} |
|
|
.simple-player-container.compact-player .play-pause-btn-simple svg { |
|
|
width: 20px; |
|
|
height: 20px; |
|
|
} |
|
|
.simple-player-container.compact-player .audio-download-btn-new { |
|
|
padding: 0.6rem 1rem; |
|
|
font-size: 0.9rem; |
|
|
max-width: 200px; |
|
|
margin-top: 5px; |
|
|
} |
|
|
.simple-player-container.compact-player .time-display { |
|
|
font-size: 0.8rem; |
|
|
min-width: 35px; |
|
|
} |
|
|
|
|
|
#standard-view .char-counter-wrapper { font-size: 0.85em; color: var(--text-tertiary); text-align: left; margin-top: 0.75rem; padding: 0 0.2rem; } |
|
|
#standard-view #char-count { font-weight: 600; color: var(--accent-primary); } |
|
|
#standard-view #selected-speaker-display { text-align: center; margin-top: 1.5rem; } |
|
|
#standard-view #selected-speaker-card { display: inline-flex; align-items: center; background: linear-gradient(135deg, var(--input-bg) 0%, var(--panel-bg) 100%); border-radius: 50px; padding: 0.75rem 0.75rem 0.75rem 1.5rem; box-shadow: var(--shadow-md); border: 1px solid var(--panel-border); transition: var(--transition-bounce); cursor: pointer; margin-bottom: 1.5rem; } |
|
|
#standard-view #selected-speaker-card:hover { transform: translateY(-6px) scale(1.03); box-shadow: var(--shadow-lg); border-color: var(--accent-primary); } |
|
|
#standard-view #selected-speaker-card img { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; margin-left: 1rem; border: 3px solid var(--accent-secondary); box-shadow: 0 0 15px -3px var(--accent-secondary-glow); transition: var(--transition-smooth); } |
|
|
#standard-view #selected-speaker-info { text-align: right; } |
|
|
#standard-view #selected-speaker-info h3 { margin: 0; font-size: 1.25em; font-weight: 800; } |
|
|
#standard-view #selected-speaker-info p { margin: 2px 0 0; color: var(--text-secondary); font-size: 0.85em; font-weight: 500; } |
|
|
#standard-view #change-speaker-btn { display: inline-flex; align-items: center; justify-content: center; padding: 12px 24px; border-radius: var(--radius-btn); background: var(--panel-bg); border: 1px solid var(--input-border); color: var(--text-primary); cursor: pointer; font-family: var(--app-font); font-weight: 600; font-size: 1em; transition: var(--transition-smooth); box-shadow: var(--shadow-md); } |
|
|
#standard-view #change-speaker-btn:hover { background: var(--input-bg); border-color: var(--accent-primary); color: var(--accent-primary); transform: translateY(-3px) scale(1.05); box-shadow: var(--shadow-lg); } |
|
|
.label-with-info { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1.2rem; } |
|
|
.label-with-info label { margin-bottom: 0; } |
|
|
.info-icon { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; background-color: var(--input-bg); border: 1px solid var(--input-border); color: var(--text-secondary); font-size: 0.9em; font-weight: 700; cursor: pointer; transition: var(--transition-smooth); user-select: none; } |
|
|
.info-icon:hover { background-color: var(--accent-primary); color: white; border-color: var(--accent-primary); transform: scale(1.1); box-shadow: 0 0 10px var(--accent-primary-glow); } |
|
|
#voice-clone-view { display: none; } |
|
|
#voice-clone-view .input-description { font-size: 0.9em; color: var(--text-secondary); margin-top: -10px; margin-bottom: 1rem; line-height: 1.7; } |
|
|
#voice-clone-view .label-subtitle { font-size: 0.85em; color: #8A94A6; font-weight: 500; margin-top: -12px; margin-bottom: 12px; } |
|
|
#voice-clone-view .upload-area { border: 2px dashed var(--input-border); border-radius: var(--radius-input); padding: 2rem; text-align: center; cursor: pointer; transition: var(--transition-smooth); background-color: var(--input-bg); } |
|
|
#voice-clone-view .upload-area:hover, #voice-clone-view .upload-area.drag-over { border-color: var(--accent-primary); background-color: #fff; box-shadow: 0 0 15px var(--accent-primary-glow); } |
|
|
#voice-clone-view .upload-icon svg { width: 48px; height: 48px; color: var(--accent-primary); margin-bottom: 1rem; stroke-width: 1.5; opacity: 0.8; } |
|
|
#voice-clone-view .upload-area p { margin: 0; color: var(--text-secondary); font-weight: 500; } |
|
|
#voice-clone-view #file-preview { display: none; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background-color: var(--input-bg); border-radius: var(--radius-input); margin-top: 1rem; animation: fadeIn 0.3s; border: 1px solid var(--panel-border); } |
|
|
#voice-clone-view #file-info { display: flex; align-items: center; gap: 1rem; overflow: hidden; } |
|
|
#voice-clone-view .preview-play-btn { background: var(--accent-secondary); color: white; border: none; border-radius: 50%; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s; flex-shrink: 0; } |
|
|
#voice-clone-view .preview-play-btn:hover { background-color: var(--accent-secondary-hover); transform: scale(1.1); } |
|
|
#voice-clone-view .preview-play-btn svg { width: 20px; height: 20px; fill: currentColor; } |
|
|
#voice-clone-view .pause-icon-preview { display: none; } |
|
|
#voice-clone-view #file-name { font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } |
|
|
#voice-clone-view #remove-file-btn { background: none; border: none; color: var(--text-tertiary); cursor: pointer; font-size: 1.5rem; transition: all 0.2s; flex-shrink: 0; } |
|
|
#voice-clone-view #remove-file-btn:hover { color: #e53e3e; transform: scale(1.1); } |
|
|
#back-to-standard-btn { position: absolute; top: 1.5rem; left: 1.5rem; background: var(--input-bg); border: 1px solid var(--panel-border); color: var(--text-secondary); font-size: 0.9em; font-weight: 600; padding: 0.6rem 1rem; border-radius: var(--radius-btn); cursor: pointer; display: flex; align-items: center; gap: 0.5rem; transition: all 0.2s ease; } |
|
|
#back-to-standard-btn:hover { background: var(--accent-primary); color: white; transform: scale(1.05); box-shadow: var(--shadow-md); } |
|
|
#back-to-standard-btn svg { width: 1.2em; height: 1.2em; } |
|
|
.modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(18, 24, 38, 0.6); backdrop-filter: blur(10px) saturate(150%); display: none; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity var(--transition-smooth); } |
|
|
.modal-overlay.visible { display: flex; opacity: 1; } |
|
|
.modal-dialog { background: var(--panel-bg); padding: 2.5rem; border-radius: var(--radius-card); width: 90%; box-shadow: var(--shadow-xl); border: 1px solid var(--panel-border); opacity: 0; animation: modalZoomIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; position: relative; } |
|
|
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid var(--panel-border); } |
|
|
.modal-header h2 { margin: 0; font-size: 1.8em; font-weight: 800; color: var(--accent-primary); } |
|
|
.close-modal-btn { background: linear-gradient(90deg, #8A2BE2, #EC4899); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; text-fill-color: transparent; border: none; font-size: 3rem; font-weight: 700; cursor: pointer; transition: var(--transition-smooth); line-height: 1; padding: 0; } |
|
|
.close-modal-btn:hover { transform: rotate(90deg) scale(1.1); opacity: 0.8; } |
|
|
#speaker-modal .modal-dialog { max-width: 750px; max-height: 85vh; overflow-y: auto; } |
|
|
#speaker-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; } |
|
|
.speaker-card { cursor: pointer; transition: var(--transition-smooth); text-align: center; position: relative; border-radius: var(--radius-card); padding: 0.5rem; } |
|
|
.speaker-card:hover { transform: translateY(-8px); } |
|
|
.speaker-card input[type="radio"] { display: none; } |
|
|
.speaker-card .speaker-visual { border: 3px solid transparent; border-radius: 18px; overflow: hidden; box-shadow: var(--shadow-md); position: relative; background-color: var(--input-bg); transition: var(--transition-bounce); } |
|
|
.speaker-card:hover .speaker-visual { box-shadow: var(--shadow-lg); } |
|
|
.speaker-card img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block; background-color: #e0e0e0; border-radius: 14px; transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); } |
|
|
.speaker-card:hover img { transform: scale(1.1); } |
|
|
.speaker-card .speaker-name { padding: 0.8rem 0.4rem 0.2rem; font-weight: 600; font-size: 0.95em; color: var(--text-secondary); transition: color 0.2s; } |
|
|
.speaker-card input[type="radio"]:checked + .speaker-visual { border-color: var(--accent-secondary); box-shadow: 0 0 25px -5px var(--accent-secondary-glow); transform: scale(1.05); } |
|
|
.special-card .speaker-visual { background: linear-gradient(135deg, hsl(230, 96%, 62%), hsl(230, 96%, 50%)); display: flex; flex-direction: column; align-items: center; justify-content: center; aspect-ratio: 1 / 1; color: white; border-color: transparent; } |
|
|
.special-card:hover .speaker-visual { background: linear-gradient(135deg, hsl(230, 96%, 65%), hsl(230, 96%, 55%)); box-shadow: 0 8px 25px -5px var(--accent-primary-glow); } |
|
|
.special-card .speaker-visual svg { width: 40%; height: 40%; opacity: 0.8; margin-bottom: 8px; } |
|
|
.special-card .speaker-name { font-weight: 700; color: var(--text-primary); } |
|
|
|
|
|
|
|
|
.add-model-card .speaker-visual { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; flex-direction: column; align-items: center; justify-content: center; aspect-ratio: 1 / 1; color: white; border-color: transparent; } |
|
|
.add-model-card:hover .speaker-visual { box-shadow: 0 8px 25px -5px rgba(118, 75, 162, 0.4); transform: scale(1.05); } |
|
|
.add-model-card .speaker-visual svg { width: 40%; height: 40%; opacity: 0.9; animation: pulse-soft 2s infinite; } |
|
|
|
|
|
.model-actions { position: absolute; top: 10px; right: 10px; display: flex; gap: 5px; opacity: 0; transition: opacity 0.3s; z-index: 5; } |
|
|
.speaker-card:hover .model-actions { opacity: 1; } |
|
|
.model-action-btn { background: rgba(0,0,0,0.6); color: white; border: none; border-radius: 50%; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: 0.2s; font-size: 14px; } |
|
|
.model-action-btn:hover { transform: scale(1.1); } |
|
|
.btn-edit:hover { background: #3B82F6; } |
|
|
.btn-delete:hover { background: #EF4444; } |
|
|
.custom-model-badge { position: absolute; bottom: 0; right: 0; background: var(--accent-secondary); color: white; padding: 2px 8px; font-size: 0.7em; border-radius: 8px 0 14px 0; font-weight: 700; z-index: 4; } |
|
|
|
|
|
|
|
|
.upgrade-modal-dark .modal-dialog { |
|
|
background-color: #0f172a; |
|
|
color: white; |
|
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
|
box-shadow: 0 0 50px rgba(139, 92, 246, 0.3); |
|
|
max-width: 380px; |
|
|
width: 90%; |
|
|
border-radius: 2.5rem; |
|
|
overflow: hidden; |
|
|
max-height: 90vh; |
|
|
overflow-y: auto; |
|
|
} |
|
|
.upgrade-modal-dark .modal-header { border-bottom: none; justify-content: flex-end; padding-bottom: 0; margin-bottom: 0.5rem; } |
|
|
.upgrade-modal-dark .close-modal-btn { color: rgba(255,255,255,0.5); font-size: 2rem; } |
|
|
.upgrade-modal-dark .close-modal-btn:hover { color: white; } |
|
|
|
|
|
.glow-bg-purple { position: absolute; top: -50px; right: -50px; width: 200px; height: 200px; background: rgba(147, 51, 234, 0.2); border-radius: 50%; filter: blur(80px); pointer-events: none; } |
|
|
.glow-bg-blue { position: absolute; bottom: -50px; left: -50px; width: 200px; height: 200px; background: rgba(37, 99, 235, 0.2); border-radius: 50%; filter: blur(80px); pointer-events: none; } |
|
|
|
|
|
.magic-icon-wrapper { position: relative; width: 80px; height: 80px; margin: 0 auto 1.2rem auto; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #1e293b, #334155); border-radius: 2rem; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); transform: rotate(6deg); transition: transform 0.3s; } |
|
|
.magic-icon-wrapper:hover { transform: rotate(12deg) scale(1.05); } |
|
|
.magic-icon-wrapper i { font-size: 2rem; background: linear-gradient(135deg, #fcd34d, #d97706); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } |
|
|
|
|
|
.upgrade-title { text-align: center; font-size: 1.25rem; font-weight: 900; margin-bottom: 1rem; line-height: 1.4; color: white; } |
|
|
.upgrade-features-box { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(10px); border-radius: 1.5rem; padding: 1.2rem; border: 1px solid rgba(255, 255, 255, 0.1); margin-bottom: 1.5rem; } |
|
|
.upgrade-desc { font-size: 0.85rem; color: #cbd5e1; line-height: 1.7; margin-bottom: 1rem; text-align: right; } |
|
|
|
|
|
.upgrade-list { display: flex; flex-direction: column; gap: 0.6rem; } |
|
|
.upgrade-item { display: flex; align-items: center; gap: 0.6rem; font-size: 0.8rem; color: #94a3b8; background: rgba(255, 255, 255, 0.05); padding: 0.6rem; border-radius: 0.8rem; } |
|
|
.upgrade-item i { color: #22c55e; flex-shrink: 0; } |
|
|
|
|
|
.upgrade-btn-action { width: 100%; padding: 1rem; border-radius: 1rem; font-weight: 900; font-size: 1rem; color: #0f172a; background: linear-gradient(90deg, #fcd34d, #f59e0b); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 0.5rem; transition: transform 0.2s, box-shadow 0.2s; box-shadow: 0 0 20px rgba(245, 158, 11, 0.2); } |
|
|
.upgrade-btn-action:hover { transform: translateY(-2px); box-shadow: 0 10px 25px -5px rgba(245, 158, 11, 0.4); } |
|
|
.upgrade-cancel-btn { width: 100%; background: none; border: none; color: #64748b; font-size: 0.8rem; font-weight: 700; padding: 0.8rem; cursor: pointer; margin-top: 0.2rem; } |
|
|
.upgrade-cancel-btn:hover { color: white; } |
|
|
|
|
|
|
|
|
@media (max-height: 720px), (max-width: 370px) { |
|
|
.upgrade-modal-dark .modal-dialog { padding: 1.5rem 1rem; margin: 1rem; } |
|
|
.magic-icon-wrapper { width: 60px; height: 60px; margin-bottom: 0.8rem; } |
|
|
.magic-icon-wrapper i { font-size: 1.5rem; } |
|
|
.upgrade-title { font-size: 1.1rem; margin-bottom: 0.8rem; } |
|
|
.upgrade-desc { font-size: 0.75rem; line-height: 1.6; margin-bottom: 0.8rem; } |
|
|
.upgrade-item { padding: 0.5rem; font-size: 0.75rem; } |
|
|
.upgrade-features-box { padding: 1rem; margin-bottom: 1rem; } |
|
|
.upgrade-btn-action { padding: 0.8rem; font-size: 0.95rem; } |
|
|
} |
|
|
|
|
|
#credit-status-message { text-align: center; color: var(--text-secondary); font-weight: 600; margin: 1rem 0; opacity: 0; transform: translateY(10px); transition: opacity 0.4s ease, transform 0.4s ease; height: 0; overflow: hidden; } |
|
|
#credit-status-message.visible { opacity: 1; transform: translateY(0); height: auto; padding: 0.5rem 0; } |
|
|
@media (max-width: 600px) { |
|
|
.page-wrapper { padding: 1.5rem 0; } |
|
|
.main-content { padding: 1.5rem; } |
|
|
.app-header h1 { font-size: 2em; } |
|
|
#standard-view #selected-speaker-card { flex-direction: column; padding: 1rem; border-radius: var(--radius-card); } |
|
|
#standard-view #selected-speaker-card img { margin-left: 0; margin-bottom: 1rem; } |
|
|
#back-to-standard-btn { top: 1rem; left: 1rem; padding: 0.5rem 0.8rem; font-size: 0.8em; } |
|
|
} |
|
|
@media (max-width: 500px) { #speaker-grid { grid-template-columns: repeat(2, 1fr); } } |
|
|
.landing-section { padding: 4rem 0; text-align: center; border-top: 1px solid var(--panel-border); } |
|
|
.landing-section .subtitle { font-size: 1.1em; color: var(--text-secondary); max-width: 600px; margin: 0 auto 3rem auto; } |
|
|
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 2rem; margin-top: 3rem; } |
|
|
.feature-card { background-color: var(--panel-bg); padding: 2rem; border-radius: var(--radius-card); box-shadow: var(--shadow-lg); border: 1px solid var(--panel-border); transition: all 0.3s ease; } |
|
|
.feature-card:hover { transform: translateY(-10px); box-shadow: var(--shadow-xl); border-color: var(--accent-primary); } |
|
|
.feature-card .icon { font-size: 2.5rem; margin-bottom: 1rem; color: var(--accent-primary); } |
|
|
.feature-card h3 { font-size: 1.3em; margin-bottom: 0.5rem; color: var(--text-primary); } |
|
|
.feature-card p { color: var(--text-secondary); font-size: 0.95em; line-height: 1.7; } |
|
|
.faq-accordion { max-width: 700px; margin: 3rem auto 0 auto; text-align: right; } |
|
|
.faq-item { background-color: var(--panel-bg); border-radius: var(--radius-input); margin-bottom: 1rem; border: 1px solid var(--panel-border); box-shadow: var(--shadow-md); } |
|
|
.landing-section.faq > h2 { font-size: 2.2em; font-weight: 800; margin-bottom: 1rem; color: #1A202C; background: none; -webkit-background-clip: unset; -webkit-text-fill-color: unset; background-clip: unset; text-fill-color: unset; display: inline-block; } |
|
|
.faq-question { width: 100%; padding: 1.2rem 1.5rem; font-family: var(--app-font); font-size: 1.1em; font-weight: 600; background: none; border: none; text-align: right; cursor: pointer; display: flex; justify-content: space-between; align-items: center; color: #1A202C !important; } |
|
|
.faq-question::after { content: '+'; font-size: 1.5em; color: var(--accent-primary); transition: transform 0.3s ease; } |
|
|
.faq-item.active .faq-question::after { transform: rotate(45deg); } |
|
|
.faq-answer { max-height: 0; overflow: hidden; transition: max-height 0.3s ease, padding 0.3s ease; color: var(--text-secondary); font-size: 1em; line-height: 1.8; } |
|
|
.faq-answer p { padding: 0 1.5rem 1.2rem 1.5rem; margin: 0; } |
|
|
.site-footer { text-align: center; padding: 2rem 0; margin-top: 3rem; border-top: 1px solid var(--panel-border); color: var(--text-tertiary); } |
|
|
.site-footer a { color: var(--accent-primary); text-decoration: none; font-weight: 600; } |
|
|
.pricing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; margin-top: 3rem; } |
|
|
.pricing-card { background-color: var(--panel-bg); padding: 2rem; border-radius: var(--radius-card); box-shadow: var(--shadow-lg); border: 2px solid var(--panel-border); text-align: center; transition: all 0.3s ease; } |
|
|
.pricing-card:hover { transform: translateY(-10px) scale(1.03); border-color: var(--accent-primary); box-shadow: var(--shadow-xl); } |
|
|
.pricing-card h3 { font-size: 1.5em; margin-top: 0; color: var(--text-primary); } |
|
|
.pricing-card .price { font-size: 2.5em; font-weight: 900; color: var(--accent-primary); margin: 1rem 0; } |
|
|
.pricing-card .price span { font-size: 0.5em; font-weight: 500; color: var(--text-secondary); } |
|
|
.pricing-card .select-plan-btn { width: 100%; padding: 1rem; margin-top: 1.5rem; font-family: var(--app-font); font-size: 1.1em; font-weight: 700; background: var(--accent-primary); color: white; border: none; border-radius: var(--radius-btn); cursor: pointer; transition: all 0.2s ease; } |
|
|
.pricing-card .select-plan-btn:hover { background: var(--accent-primary-hover); transform: translateY(-3px); box-shadow: 0 6px 15px var(--accent-primary-glow); } |
|
|
@media (max-width: 768px) { .pricing-grid { grid-template-columns: 1fr; } } |
|
|
.header-actions { margin-top: 1rem; text-align: center;} |
|
|
#user-status-container { |
|
|
padding: 0.75rem 1.5rem; |
|
|
background-color: var(--input-bg); |
|
|
border-radius: var(--radius-btn); |
|
|
display: none; |
|
|
flex-direction: column; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
gap: 0.5rem; |
|
|
animation: fadeIn 0.5s; |
|
|
border: 1px solid var(--panel-border); |
|
|
max-width: 320px; |
|
|
margin: 0 auto; |
|
|
} |
|
|
#user-status-details { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 1rem; |
|
|
} |
|
|
#user-status-container .user-email { font-weight: 700; color: var(--text-primary); } |
|
|
#user-status-container .user-sub-status { font-weight: 600; padding: 0.3rem 1rem; border-radius: 20px; font-size: 0.9em; white-space: nowrap; } |
|
|
.status-paid { background-color: var(--accent-secondary-glow); color: var(--accent-secondary-hover); border: 1px solid var(--accent-secondary); } |
|
|
.status-free { background-color: #e2e8f0; color: #4a5568; } |
|
|
#user-status-container .logout-btn { background: none; border: none; color: var(--text-tertiary); cursor: pointer; font-weight: 600; font-size: 0.9em; } |
|
|
#user-status-container .logout-btn:hover { color: #e53e3e; } |
|
|
#login-check-btn { |
|
|
background: var(--input-bg); |
|
|
border: 1px solid var(--panel-border); |
|
|
color: var(--text-primary); |
|
|
font-size: 1em; |
|
|
font-weight: 600; |
|
|
padding: 0.75rem 1.5rem; |
|
|
border-radius: var(--radius-btn); |
|
|
cursor: pointer; |
|
|
transition: var(--transition-smooth); |
|
|
display: inline-block; |
|
|
} |
|
|
#login-check-btn:hover { |
|
|
background: var(--accent-primary); |
|
|
color: white; |
|
|
transform: translateY(-3px); |
|
|
box-shadow: var(--shadow-lg); |
|
|
} |
|
|
@keyframes pulse-glow { |
|
|
0%, 100% { box-shadow: 0 0 20px rgba(29, 161, 242, 0.4); } |
|
|
50% { box-shadow: 0 0 35px rgba(29, 161, 242, 0.8); } |
|
|
} |
|
|
.contact-section { |
|
|
background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%); |
|
|
padding: 3rem 2rem; |
|
|
margin-top: 4rem; |
|
|
border-radius: var(--radius-card); |
|
|
text-align: center; |
|
|
color: white; |
|
|
box-shadow: var(--shadow-xl); |
|
|
} |
|
|
.contact-section h2 { |
|
|
margin: 0 0 1rem 0; |
|
|
font-size: 2em; |
|
|
font-weight: 800; |
|
|
text-shadow: 0 2px 4px rgba(0,0,0,0.2); |
|
|
} |
|
|
.contact-section p { |
|
|
margin: 0 0 2rem 0; |
|
|
opacity: 0.9; |
|
|
font-size: 1.1em; |
|
|
} |
|
|
.contact-button { |
|
|
display: inline-flex; |
|
|
align-items: center; |
|
|
gap: 12px; |
|
|
padding: 1rem 2rem; |
|
|
background-color: white; |
|
|
color: #1DA1F2; |
|
|
border-radius: var(--radius-btn); |
|
|
text-decoration: none; |
|
|
font-weight: 700; |
|
|
font-size: 1.1em; |
|
|
box-shadow: var(--shadow-lg); |
|
|
transition: all 0.3s ease; |
|
|
animation: pulse-glow 3s infinite ease-in-out; |
|
|
} |
|
|
.contact-button:hover { |
|
|
transform: translateY(-5px) scale(1.05); |
|
|
box-shadow: var(--shadow-xl); |
|
|
background-color: #f0f8ff; |
|
|
} |
|
|
.contact-button svg { |
|
|
width: 24px; |
|
|
height: 24px; |
|
|
} |
|
|
@media (min-width: 500px) { |
|
|
#user-status-container { |
|
|
flex-direction: row; |
|
|
gap: 1rem; |
|
|
} |
|
|
} |
|
|
|
|
|
#cf-container-standard, #cf-container-clone, #cf-container-login { |
|
|
margin: 1.5rem 0; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
} |
|
|
|
|
|
|
|
|
.history-container { |
|
|
margin-top: 2rem; |
|
|
border-top: 1px solid var(--panel-border); |
|
|
padding-top: 2rem; |
|
|
} |
|
|
.history-header { |
|
|
display: flex; align-items: center; gap: 10px; |
|
|
color: var(--text-primary); margin-bottom: 1.5rem; |
|
|
font-size: 1.2rem; font-weight: 800; |
|
|
border-right: 4px solid var(--accent-primary); |
|
|
padding-right: 10px; |
|
|
justify-content: space-between; |
|
|
} |
|
|
|
|
|
.history-list { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
gap: 1rem; |
|
|
} |
|
|
|
|
|
|
|
|
.request-card { |
|
|
background: var(--card-bg); |
|
|
border-radius: 16px; |
|
|
padding: 1.5rem; |
|
|
margin-bottom: 1.2rem; |
|
|
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05), 0 2px 4px -1px rgba(0,0,0,0.03); |
|
|
border: 1px solid #E2E8F0; |
|
|
position: relative; |
|
|
overflow: hidden; |
|
|
transition: transform 0.2s; |
|
|
} |
|
|
.request-card:hover { transform: translateY(-2px); box-shadow: 0 10px 15px -3px rgba(0,0,0,0.08); } |
|
|
|
|
|
.card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem; } |
|
|
.card-info { display: flex; flex-direction: column; gap: 4px; } |
|
|
.project-name { font-weight: 700; font-size: 1.1rem; color: var(--text-primary); margin: 0; line-height: 1.4; } |
|
|
.project-date { font-size: 0.85rem; color: var(--text-secondary); } |
|
|
|
|
|
|
|
|
.delete-trigger { |
|
|
background: transparent !important; |
|
|
border: none; |
|
|
cursor: pointer; |
|
|
color: #ff0000 !important; |
|
|
transition: 0.2s; |
|
|
padding: 8px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
border-radius: 50%; |
|
|
} |
|
|
.delete-trigger:hover { |
|
|
background-color: #ffe5e5 !important; |
|
|
transform: scale(1.1); |
|
|
} |
|
|
.delete-trigger svg { |
|
|
stroke-width: 2.5; |
|
|
stroke: currentColor; |
|
|
} |
|
|
|
|
|
|
|
|
.processing-content { text-align: center; width: 100%; } |
|
|
.progress-track { |
|
|
background: #EDF2F7; height: 10px; width: 100%; border-radius: 10px; |
|
|
overflow: hidden; margin-bottom: 10px; position: relative; |
|
|
} |
|
|
.progress-bar { |
|
|
height: 100%; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); |
|
|
width: 0%; border-radius: 10px; |
|
|
transition: width 0.5s ease; |
|
|
position: relative; |
|
|
} |
|
|
.progress-bar::after { |
|
|
content: ''; position: absolute; top: 0; left: 0; bottom: 0; right: 0; |
|
|
background-image: linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent); |
|
|
background-size: 1rem 1rem; animation: progress-stripes 1s linear infinite; |
|
|
} |
|
|
@keyframes progress-stripes { from { background-position: 1rem 0; } to { background-position: 0 0; } } |
|
|
|
|
|
.progress-text { display: flex; justify-content: space-between; font-size: 0.85rem; font-weight: 700; color: var(--accent-primary); margin-bottom: 8px; } |
|
|
|
|
|
.info-box { |
|
|
background: #EBF8FF; border: 1px solid #BEE3F8; color: #2C5282; |
|
|
padding: 10px; border-radius: 8px; font-size: 0.8rem; line-height: 1.6; |
|
|
display: flex; align-items: flex-start; gap: 8px; text-align: right; |
|
|
} |
|
|
.info-icon { font-size: 1.1rem; } |
|
|
|
|
|
|
|
|
.delete-overlay { |
|
|
position: absolute; top: 0; left: 0; width: 100%; height: 100%; |
|
|
background: rgba(255, 255, 255, 0.95); |
|
|
backdrop-filter: blur(4px); |
|
|
display: flex; flex-direction: column; align-items: center; justify-content: center; |
|
|
opacity: 0; pointer-events: none; transition: 0.3s; |
|
|
transform: translateY(10px); |
|
|
z-index: 10; |
|
|
border-radius: 16px; |
|
|
} |
|
|
.request-card.deleting .delete-overlay { opacity: 1; pointer-events: auto; transform: translateY(0); } |
|
|
|
|
|
.delete-text { color: #000000 !important; font-weight: 800; margin-bottom: 1.5rem; font-size: 1rem; } |
|
|
.delete-actions { display: flex; gap: 1rem; } |
|
|
|
|
|
|
|
|
.btn-cancel { |
|
|
background: #4A5568 !important; |
|
|
color: #ffffff !important; |
|
|
border: 2px solid #2D3748 !important; |
|
|
padding: 10px 24px; |
|
|
border-radius: 10px; |
|
|
cursor: pointer; |
|
|
font-family: inherit; |
|
|
font-size: 0.95rem; |
|
|
font-weight: 600; |
|
|
transition: all 0.2s; |
|
|
} |
|
|
.btn-cancel:hover { background: #2D3748 !important; } |
|
|
|
|
|
|
|
|
.btn-confirm { |
|
|
background: #E53E3E !important; |
|
|
color: white !important; |
|
|
border: none; |
|
|
padding: 10px 24px; |
|
|
border-radius: 10px; |
|
|
cursor: pointer; |
|
|
font-family: inherit; |
|
|
font-size: 0.95rem; |
|
|
font-weight: 600; |
|
|
transition: all 0.2s; |
|
|
box-shadow: 0 4px 6px rgba(229, 62, 62, 0.3); |
|
|
} |
|
|
.btn-confirm:hover { background: #c53030 !important; transform: translateY(-2px); } |
|
|
|
|
|
|
|
|
#upload-guide-modal .modal-dialog { |
|
|
max-width: 420px; |
|
|
padding: 1.5rem; |
|
|
border-radius: 20px; |
|
|
text-align: center; |
|
|
} |
|
|
.guide-header { |
|
|
text-align: center; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
.guide-icon-animated { |
|
|
width: 55px; height: 55px; |
|
|
background: linear-gradient(135deg, #e0e7ff, #f3f4f6); |
|
|
color: var(--accent-primary); |
|
|
border-radius: 50%; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
margin: 0 auto 10px auto; |
|
|
box-shadow: 0 5px 15px -3px var(--accent-primary-glow); |
|
|
} |
|
|
.guide-icon-animated svg { |
|
|
width: 28px; height: 28px; |
|
|
animation: pulse-soft 2s infinite; |
|
|
} |
|
|
.guide-title { |
|
|
font-size: 1.1rem; |
|
|
font-weight: 800; |
|
|
margin: 0; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
.guide-content { |
|
|
font-size: 0.9rem; |
|
|
line-height: 1.7; |
|
|
color: var(--text-secondary); |
|
|
background: #F8F9FC; |
|
|
padding: 12px; |
|
|
border-radius: 12px; |
|
|
margin-bottom: 12px; |
|
|
text-align: justify; |
|
|
} |
|
|
.guide-alert { |
|
|
background: #FFF7ED; |
|
|
border: 1px dashed #FDBA74; |
|
|
padding: 10px; |
|
|
border-radius: 10px; |
|
|
font-size: 0.8rem; |
|
|
display: flex; gap: 8px; |
|
|
margin-bottom: 1rem; |
|
|
color: #9A3412; |
|
|
align-items: flex-start; |
|
|
text-align: right; |
|
|
} |
|
|
#upload-guide-confirm-btn { |
|
|
width: 100%; |
|
|
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); |
|
|
color: white; |
|
|
font-weight: 700; |
|
|
padding: 0.8rem; |
|
|
border-radius: 12px; |
|
|
border: none; |
|
|
cursor: pointer; |
|
|
font-size: 1rem; |
|
|
transition: transform 0.2s; |
|
|
} |
|
|
#upload-guide-confirm-btn:hover { |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
|
|
|
|
|
|
@media (max-width: 380px) { |
|
|
#upload-guide-modal .modal-dialog { padding: 1rem; } |
|
|
.guide-icon-animated { width: 45px; height: 45px; margin-bottom: 8px; } |
|
|
.guide-icon-animated svg { width: 22px; height: 22px; } |
|
|
.guide-title { font-size: 1rem; } |
|
|
.guide-content { font-size: 0.85rem; padding: 10px; line-height: 1.6; } |
|
|
.guide-alert { font-size: 0.75rem; padding: 8px; } |
|
|
#upload-guide-confirm-btn { padding: 0.7rem; font-size: 0.95rem; } |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
|
|
|
<div class="page-wrapper"> |
|
|
<div class="app-container"> |
|
|
|
|
|
<header class="app-header"> |
|
|
<h1>هوش مصنوعی آلفا</h1> |
|
|
<div id="user-status-container"> |
|
|
<span id="user-email-display"></span> |
|
|
<button id="logout-btn" style="background:none;border:none;color:red;cursor:pointer;">خروج</button> |
|
|
</div> |
|
|
<button id="login-check-btn" class="generate-btn" style="width: auto; margin: 0 auto; padding: 0.5rem 1.5rem;">ورود / ثبت نام</button> |
|
|
</header> |
|
|
|
|
|
<div class="nav-tabs"> |
|
|
<button class="nav-btn" onclick="switchTab('standard')">متن به صدا</button> |
|
|
<button class="nav-btn active" onclick="switchTab('clone')">شبیهسازی صدا</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="standard-view" style="text-align:center; padding: 2rem;"> |
|
|
<p>برای بخش متن به صدا، به کد اصلی مراجعه کنید. تمرکز این فایل روی رفع مشکل شبیهسازی است.</p> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="voice-clone-view"> |
|
|
<main class="main-content"> |
|
|
<form id="voice-clone-form" onsubmit="return false;"> |
|
|
<div class="form-group"> |
|
|
<label>📝 متن اصلی</label> |
|
|
<textarea id="text-input-clone" placeholder="متنی که میخواهید با صدای خودتان خوانده شود..."></textarea> |
|
|
</div> |
|
|
|
|
|
<div class="form-group"> |
|
|
<label>🎤 صدای شما (مرجع)</label> |
|
|
<label class="upload-area" id="upload-area"> |
|
|
<div>📂</div> |
|
|
<p>فایل صوتی خود را اینجا بکشید یا کلیک کنید (۳ تا ۱۰ ثانیه، فرمت WAV/MP3)</p> |
|
|
<input type="file" id="user-voice-input" accept="audio/*" style="display: none;"> |
|
|
</label> |
|
|
<div id="file-preview"> |
|
|
<span id="file-name"></span> |
|
|
<button type="button" id="remove-file-btn" style="background:none;border:none;cursor:pointer;">✕</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="cf-container-clone"></div> |
|
|
|
|
|
<button type="submit" id="generate-btn-clone" class="generate-btn"> |
|
|
<span class="btn-text">شروع پردازش</span> |
|
|
<div class="spinner"></div> |
|
|
</button> |
|
|
</form> |
|
|
|
|
|
<div style="margin-top: 2rem;"> |
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;"> |
|
|
<h3>تاریخچه درخواستها</h3> |
|
|
<button id="clear-history" style="background:none;border:none;color:gray;cursor:pointer;">پاکسازی</button> |
|
|
</div> |
|
|
<div id="history-list"></div> |
|
|
</div> |
|
|
</main> |
|
|
</div> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="email-modal" class="modal-overlay"> |
|
|
<div class="modal-dialog"> |
|
|
<button class="close-modal-btn" onclick="document.getElementById('email-modal').classList.remove('visible')">×</button> |
|
|
<h2>ورود به حساب</h2> |
|
|
<form id="email-form"> |
|
|
<input type="email" id="login-email-input" placeholder="ایمیل خود را وارد کنید" required style="margin-bottom:1rem;"> |
|
|
<div id="cf-container-login"></div> |
|
|
<button type="submit" class="generate-btn">ارسال کد</button> |
|
|
</form> |
|
|
<form id="code-form" style="display:none; margin-top:1rem;"> |
|
|
<input type="text" id="code-input" placeholder="کد تایید" required style="margin-bottom:1rem;"> |
|
|
<button type="submit" class="generate-btn">تایید</button> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
const PROXY_URL = '/tts/proxy.php'; |
|
|
let widgetIdClone, widgetIdLogin; |
|
|
let currentUser = { email: localStorage.getItem('userEmail'), status: 'unknown' }; |
|
|
|
|
|
|
|
|
function switchTab(tab) { |
|
|
document.getElementById('standard-view').style.display = tab === 'standard' ? 'block' : 'none'; |
|
|
document.getElementById('voice-clone-view').style.display = tab === 'clone' ? 'block' : 'none'; |
|
|
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); |
|
|
event.target.classList.add('active'); |
|
|
} |
|
|
|
|
|
function updateAuthUI() { |
|
|
if(currentUser.email) { |
|
|
document.getElementById('user-email-display').textContent = currentUser.email; |
|
|
document.getElementById('user-status-container').style.display = 'flex'; |
|
|
document.getElementById('login-check-btn').style.display = 'none'; |
|
|
} else { |
|
|
document.getElementById('user-status-container').style.display = 'none'; |
|
|
document.getElementById('login-check-btn').style.display = 'block'; |
|
|
} |
|
|
} |
|
|
updateAuthUI(); |
|
|
|
|
|
|
|
|
function getJobs() { return JSON.parse(localStorage.getItem('aisada_jobs_v2') || '[]'); } |
|
|
function saveJob(job) { |
|
|
let jobs = getJobs(); |
|
|
const existingIndex = jobs.findIndex(j => j.id === job.id); |
|
|
if(existingIndex > -1) jobs[existingIndex] = job; |
|
|
else jobs.unshift(job); |
|
|
localStorage.setItem('aisada_jobs_v2', JSON.stringify(jobs)); |
|
|
renderHistory(); |
|
|
} |
|
|
|
|
|
function renderHistory() { |
|
|
const list = document.getElementById('history-list'); |
|
|
list.innerHTML = ''; |
|
|
const jobs = getJobs(); |
|
|
|
|
|
jobs.forEach(job => { |
|
|
let statusHtml = ''; |
|
|
let contentHtml = ''; |
|
|
|
|
|
if(job.status === 'completed') { |
|
|
statusHtml = '<span class="project-status status-completed">تکمیل شد</span>'; |
|
|
const dlUrl = `${PROXY_URL}?endpoint=download-clone&filename=${job.filename}`; |
|
|
contentHtml = ` |
|
|
<audio controls src="${dlUrl}" class="simple-player"></audio> |
|
|
<a href="${dlUrl}" class="download-btn">دانلود فایل نهایی</a> |
|
|
`; |
|
|
} else if(job.status === 'failed') { |
|
|
statusHtml = '<span class="project-status status-failed">خطا</span>'; |
|
|
contentHtml = `<p style="color:red;font-size:0.9rem;">خطا: ${job.error || 'ناشناخته'}</p>`; |
|
|
} else { |
|
|
statusHtml = '<span class="project-status status-processing">در حال پردازش</span>'; |
|
|
contentHtml = ` |
|
|
<div class="progress-bar"><div class="progress-fill" style="width:${job.progress || 10}%"></div></div> |
|
|
<p style="font-size:0.8rem;color:gray;margin-top:5px;">${job.step_desc || 'در حال انجام کار...'}</p> |
|
|
`; |
|
|
} |
|
|
|
|
|
const div = document.createElement('div'); |
|
|
div.className = 'request-card'; |
|
|
div.innerHTML = ` |
|
|
<div class="card-header"> |
|
|
<span class="project-name">${job.text_preview}</span> |
|
|
${statusHtml} |
|
|
</div> |
|
|
${contentHtml} |
|
|
`; |
|
|
list.appendChild(div); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const fileInput = document.getElementById('user-voice-input'); |
|
|
const uploadArea = document.getElementById('upload-area'); |
|
|
document.getElementById('remove-file-btn').addEventListener('click', () => { |
|
|
fileInput.value = ''; |
|
|
document.getElementById('file-preview').style.display = 'none'; |
|
|
uploadArea.style.display = 'block'; |
|
|
}); |
|
|
fileInput.addEventListener('change', () => { |
|
|
if(fileInput.files[0]) { |
|
|
document.getElementById('file-name').textContent = fileInput.files[0].name; |
|
|
uploadArea.style.display = 'none'; |
|
|
document.getElementById('file-preview').style.display = 'flex'; |
|
|
} |
|
|
}); |
|
|
uploadArea.addEventListener('click', () => fileInput.click()); |
|
|
|
|
|
|
|
|
async function orchestrateClone(text, file, turnstileToken) { |
|
|
const jobId = 'job_' + Date.now(); |
|
|
const newJob = { |
|
|
id: jobId, |
|
|
text_preview: text.substring(0, 20) + '...', |
|
|
status: 'processing', |
|
|
progress: 5, |
|
|
step_desc: 'تولید صدای پایه (TTS)...' |
|
|
}; |
|
|
saveJob(newJob); |
|
|
|
|
|
try { |
|
|
|
|
|
const ttsParams = { |
|
|
text: text, |
|
|
speaker: 'Charon', |
|
|
temperature: 0.1, |
|
|
email: currentUser.email, |
|
|
turnstile_token: turnstileToken, |
|
|
fingerprint: 'browser_fp' |
|
|
}; |
|
|
|
|
|
const ttsInitRes = await fetch(`${PROXY_URL}?endpoint=generate`, { |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
body: JSON.stringify(ttsParams) |
|
|
}); |
|
|
|
|
|
if(!ttsInitRes.ok) throw new Error('خطا در شروع تولید صدا'); |
|
|
const ttsInitData = await ttsInitRes.json(); |
|
|
const ttsJobId = ttsInitData.job_id; |
|
|
|
|
|
|
|
|
let finalTtsBlob = null; |
|
|
for(let i=0; i<60; i++) { |
|
|
newJob.progress = 10 + Math.floor(i * 1.5); |
|
|
newJob.step_desc = 'در حال ساخت صدای پایه...'; |
|
|
saveJob(newJob); |
|
|
|
|
|
const pollRes = await fetch(`${PROXY_URL}?endpoint=check-tts-status`, { |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
body: JSON.stringify({job_id: ttsJobId}) |
|
|
}); |
|
|
|
|
|
|
|
|
const contentType = pollRes.headers.get("content-type"); |
|
|
if (contentType && (contentType.includes("audio") || contentType.includes("octet-stream"))) { |
|
|
finalTtsBlob = await pollRes.blob(); |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
const resClone = pollRes.clone(); |
|
|
const pollData = await resClone.json(); |
|
|
|
|
|
if(pollData.status === 'completed' && pollData.proxy_url) { |
|
|
const ttsBase = 'https://ezmary-padgenpro2.hf.space'; |
|
|
const audioUrl = pollData.proxy_url.startsWith('http') ? pollData.proxy_url : (ttsBase + pollData.proxy_url); |
|
|
const audioRes = await fetch(audioUrl); |
|
|
if(audioRes.ok) { |
|
|
finalTtsBlob = await audioRes.blob(); |
|
|
break; |
|
|
} |
|
|
} |
|
|
if(pollData.status === 'failed') throw new Error('تولید صدای پایه ناموفق بود.'); |
|
|
} catch (e) { |
|
|
|
|
|
if (e.name === 'SyntaxError') { |
|
|
console.warn("JSON parse failed, assuming binary audio received."); |
|
|
const potentialBlob = await pollRes.blob(); |
|
|
if (potentialBlob.size > 1000) { |
|
|
finalTtsBlob = potentialBlob; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
if(e.message === 'تولید صدای پایه ناموفق بود.') throw e; |
|
|
} |
|
|
|
|
|
await new Promise(r => setTimeout(r, 3000)); |
|
|
} |
|
|
|
|
|
if(!finalTtsBlob) throw new Error('تایماوت در تولید صدا.'); |
|
|
|
|
|
|
|
|
newJob.step_desc = 'ارسال به موتور شبیهسازی...'; |
|
|
newJob.progress = 75; |
|
|
saveJob(newJob); |
|
|
|
|
|
const formData = new FormData(); |
|
|
formData.append('email', currentUser.email); |
|
|
formData.append('source_audio', finalTtsBlob, 'source.wav'); |
|
|
formData.append('ref_audio', file, 'ref.wav'); |
|
|
|
|
|
const vcUploadRes = await fetch(`${PROXY_URL}?endpoint=vc-upload`, { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}); |
|
|
if(!vcUploadRes.ok) throw new Error('خطا در ارسال به سرور کلون'); |
|
|
const vcInitData = await vcUploadRes.json(); |
|
|
const vcJobId = vcInitData.job_id; |
|
|
|
|
|
|
|
|
for(let i=0; i<40; i++) { |
|
|
newJob.progress = 80 + Math.floor(i * 0.5); |
|
|
newJob.step_desc = 'نهاییسازی شبیهسازی...'; |
|
|
saveJob(newJob); |
|
|
|
|
|
const vcPollRes = await fetch(`${PROXY_URL}?endpoint=vc-status`, { |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
body: JSON.stringify({ |
|
|
job_id: vcJobId, |
|
|
total_chunks: vcInitData.total_chunks || 1, |
|
|
chunks: vcInitData.chunks || [] |
|
|
}) |
|
|
}); |
|
|
const vcData = await vcPollRes.json(); |
|
|
|
|
|
if(vcData.status === 'completed') { |
|
|
newJob.status = 'completed'; |
|
|
newJob.progress = 100; |
|
|
newJob.filename = vcData.filename; |
|
|
saveJob(newJob); |
|
|
return; |
|
|
} |
|
|
if(vcData.status === 'failed') throw new Error('خطا در موتور شبیهسازی'); |
|
|
|
|
|
await new Promise(r => setTimeout(r, 3000)); |
|
|
} |
|
|
throw new Error('تایماوت در شبیهسازی نهایی.'); |
|
|
|
|
|
} catch(e) { |
|
|
newJob.status = 'failed'; |
|
|
newJob.error = e.message; |
|
|
saveJob(newJob); |
|
|
} finally { |
|
|
const btn = document.getElementById('generate-btn-clone'); |
|
|
if(btn) { |
|
|
btn.disabled = false; |
|
|
btn.querySelector('.btn-text').textContent = 'شروع پردازش'; |
|
|
} |
|
|
if(window.turnstile && widgetIdClone) turnstile.reset(widgetIdClone); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('voice-clone-form').addEventListener('submit', async () => { |
|
|
if(!currentUser.email) { |
|
|
document.getElementById('email-modal').classList.add('visible'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const text = document.getElementById('text-input-clone').value; |
|
|
const file = document.getElementById('user-voice-input').files[0]; |
|
|
|
|
|
if(!text.trim() || !file) { |
|
|
alert('لطفا متن و فایل صوتی را وارد کنید.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const token = turnstile.getResponse(widgetIdClone); |
|
|
if(!token) { alert('کپچا را حل کنید'); return; } |
|
|
|
|
|
|
|
|
const btn = document.getElementById('generate-btn-clone'); |
|
|
btn.disabled = true; |
|
|
btn.querySelector('.btn-text').textContent = 'درخواست ارسال شد'; |
|
|
|
|
|
|
|
|
orchestrateClone(text, file, token).then(() => { |
|
|
btn.disabled = false; |
|
|
btn.querySelector('.btn-text').textContent = 'شروع پردازش'; |
|
|
turnstile.reset(widgetIdClone); |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.getElementById('clear-history').addEventListener('click', () => { |
|
|
localStorage.removeItem('aisada_jobs_v2'); |
|
|
renderHistory(); |
|
|
}); |
|
|
|
|
|
|
|
|
renderHistory(); |
|
|
setTimeout(() => { |
|
|
if(window.turnstile) { |
|
|
widgetIdClone = turnstile.render('#cf-container-clone', { sitekey: '0x4AAAAAACJYw8vz3QHa-WFi' }); |
|
|
widgetIdLogin = turnstile.render('#cf-container-login', { sitekey: '0x4AAAAAACJYw8vz3QHa-WFi' }); |
|
|
} |
|
|
}, 1000); |
|
|
|
|
|
|
|
|
document.getElementById('login-check-btn').addEventListener('click', () => document.getElementById('email-modal').classList.add('visible')); |
|
|
document.getElementById('logout-btn').addEventListener('click', () => { |
|
|
localStorage.removeItem('userEmail'); |
|
|
location.reload(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('email-form').addEventListener('submit', async (e) => { |
|
|
e.preventDefault(); |
|
|
const email = document.getElementById('login-email-input').value; |
|
|
const token = turnstile.getResponse(widgetIdLogin); |
|
|
if(!token) return alert('کپچا؟'); |
|
|
|
|
|
|
|
|
await fetch('/tts/send_code.php', { |
|
|
method: 'POST', |
|
|
body: JSON.stringify({email, turnstile_token: token}) |
|
|
}); |
|
|
document.getElementById('email-form').style.display='none'; |
|
|
document.getElementById('code-form').style.display='block'; |
|
|
}); |
|
|
|
|
|
document.getElementById('code-form').addEventListener('submit', async (e) => { |
|
|
e.preventDefault(); |
|
|
const email = document.getElementById('login-email-input').value; |
|
|
const code = document.getElementById('code-input').value; |
|
|
|
|
|
const res = await fetch('/tts/verify_code.php', { |
|
|
method: 'POST', |
|
|
body: JSON.stringify({email, code}) |
|
|
}); |
|
|
const d = await res.json(); |
|
|
if(d.status === 'success') { |
|
|
localStorage.setItem('userEmail', email); |
|
|
currentUser.email = email; |
|
|
currentUser.status = d.status_type || 'free'; |
|
|
updateAuthUI(); |
|
|
document.getElementById('email-modal').classList.remove('visible'); |
|
|
} else { |
|
|
alert('کد اشتباه است'); |
|
|
} |
|
|
}); |
|
|
|
|
|
</script> |
|
|
|
|
|
</body> |
|
|
</html> |