Spaces:
Running
Running
Luigi
commited on
Update index.html
Browse files- index.html +296 -181
index.html
CHANGED
@@ -2,71 +2,159 @@
|
|
2 |
<html lang="en">
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title>Autotutorial.ai
|
|
|
|
|
|
|
7 |
<!-- Radix UI Styles -->
|
8 |
<link rel="stylesheet" href="https://unpkg.com/@radix-ui/[email protected]/dist/index.css">
|
9 |
<!-- Tailwind CSS CDN -->
|
10 |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
|
11 |
<!-- Font Awesome CDN -->
|
12 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
13 |
-
<!-- Inter &
|
14 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
15 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
16 |
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=
|
|
|
17 |
<style>
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
footer a { color: var(--gray-600); text-decoration: none; }
|
41 |
footer a:hover { text-decoration: underline; }
|
42 |
-
|
43 |
-
.tutorial-item
|
|
|
44 |
.tutorial-item p { @apply text-gray-600 text-sm; }
|
45 |
-
.tutorial-list-section { display:
|
46 |
-
.settings-grid { display: grid; grid-template-columns: 1fr
|
47 |
-
|
|
|
|
|
|
|
48 |
|
49 |
-
/* Radix UI Button Styling */
|
50 |
.radix-button {
|
51 |
-
@apply inline-flex items-center justify-center rounded-md px-
|
|
|
52 |
}
|
53 |
-
.radix-button-primary { @apply radix-button bg-indigo-600 hover:bg-indigo-700 active:bg-indigo-800 text-white shadow-sm hover:shadow-md; }
|
54 |
.radix-button-primary:disabled { @apply bg-indigo-300 cursor-not-allowed hover:bg-indigo-300 focus:ring-0 shadow-none; }
|
55 |
-
.radix-button-secondary { @apply radix-button bg-gray-200 hover:bg-gray-300 active:bg-gray-400 text-gray-700 shadow-sm hover:shadow-md; }
|
56 |
-
.radix-button-apply { @apply radix-button bg-green-500 hover:bg-green-600 active:bg-green-700 text-white shadow-sm hover:shadow-md; }
|
57 |
|
58 |
-
/* Radix UI Input Styling */
|
59 |
.radix-input {
|
60 |
-
@apply
|
61 |
border-color: var(--gray-300);
|
62 |
-
background-color: var(--
|
63 |
color: var(--gray-900);
|
|
|
64 |
}
|
65 |
-
.radix-input:focus { @apply shadow-
|
66 |
|
67 |
-
/* Radix UI Select Styling
|
68 |
.radix-select-trigger {
|
69 |
@apply radix-button radix-button-secondary w-full justify-between;
|
|
|
|
|
70 |
}
|
71 |
.radix-select-content {
|
72 |
@apply rounded-md shadow-md bg-white border border-gray-200 overflow-hidden;
|
@@ -76,54 +164,74 @@
|
|
76 |
}
|
77 |
.radix-select-item[data-highlighted] { @apply bg-indigo-50 text-indigo-900; }
|
78 |
|
79 |
-
/* Progress Bar */
|
80 |
-
.status-progress-bar { position: absolute; bottom: 0; left: 0; right: 0; height:
|
81 |
.status-progress-bar.active { transform: scaleX(1); }
|
82 |
|
83 |
/* Transitions */
|
84 |
.transition-opacity { transition-property: opacity; transition-timing-function: ease-out; transition-duration: 200ms; }
|
85 |
|
86 |
-
/*
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
</style>
|
94 |
</head>
|
95 |
-
<body class="bg-gray-
|
96 |
<div class="app-container">
|
97 |
-
<!-- Header Section -->
|
98 |
<header>
|
99 |
-
<h1 class="
|
100 |
-
<p
|
101 |
</header>
|
102 |
|
103 |
-
<!-- Navigation Bar -->
|
104 |
-
<nav>
|
105 |
-
<ul>
|
106 |
-
<li><a href="#generate" class="nav-link active" data-section="generate">Generate Tutorial</a></li>
|
107 |
-
<li><a href="#history" class="nav-link" data-section="history">Tutorial History</a></li>
|
108 |
-
<li><a href="#settings" class="nav-link" data-section="settings">Settings</a></li>
|
109 |
-
<li><a href="#help" class="nav-link" data-section="help">Help</a></li>
|
110 |
-
</ul>
|
111 |
-
</nav>
|
112 |
-
|
113 |
<!-- Main Content Area -->
|
114 |
<main>
|
115 |
<!-- Generate Tutorial Section -->
|
116 |
<section id="generate-section">
|
117 |
-
<h2 class="text-
|
118 |
-
<p class="text-gray-700 mb-
|
119 |
|
120 |
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-
|
121 |
<div>
|
122 |
-
<label for="tutorialTopic" class="input-label">
|
123 |
-
<input type="text" id="tutorialTopic" class="radix-input" placeholder="Enter
|
124 |
</div>
|
125 |
<div>
|
126 |
-
<label for="desiredDuration" class="input-label">
|
127 |
<select id="desiredDuration" class="radix-select-trigger">
|
128 |
<option value="5" class="radix-select-item">5 Minutes</option>
|
129 |
<option value="10" selected class="radix-select-item">10 Minutes</option>
|
@@ -133,51 +241,50 @@
|
|
133 |
</div>
|
134 |
</div>
|
135 |
|
136 |
-
<section class="mb-
|
137 |
-
<h3 class="text-
|
138 |
<div class="settings-grid">
|
139 |
<div>
|
140 |
-
<label for="openaiApiKey" class="input-label">OpenAI
|
141 |
-
<input type="text" id="openaiApiKey" class="radix-input" placeholder="
|
142 |
</div>
|
143 |
<div>
|
144 |
-
<label for="videoGenApiKey" class="input-label">Video
|
145 |
-
<input type="text" id="videoGenApiKey" class="radix-input" placeholder="
|
146 |
</div>
|
147 |
</div>
|
148 |
-
<p class="text-sm text-gray-500 mt-2">
|
149 |
</section>
|
150 |
|
151 |
-
<section class="mb-
|
152 |
-
<button id="generateButton" class="radix-button-primary"><i class="fas fa-magic mr-2"></i> Generate
|
153 |
<button id="resetButton" class="radix-button-secondary hidden"><i class="fas fa-redo mr-2"></i> Reset</button>
|
154 |
</section>
|
155 |
|
156 |
<section id="statusSection" class="status-box">
|
157 |
-
<h3 class="font-semibold text-gray-700 mb-
|
158 |
<div id="statusMessages">
|
159 |
-
<p class="text-gray-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Ready
|
160 |
</div>
|
161 |
<p id="errorMessage" class="error-text hidden"></p>
|
162 |
-
<div id="progressBar" class="status-progress-bar"></div>
|
163 |
</section>
|
164 |
|
165 |
<section id="videoOutputSection" class="hidden transition-opacity opacity-0 duration-200">
|
166 |
-
<h2 class="text-
|
167 |
<div class="video-player-container">
|
168 |
<video id="tutorialVideo" controls width="100%">
|
169 |
-
<source src="https://joy.videvo.net/videvo_files/video/free/2016-06/small_watermarked/Drone_Flying_Above_City_Skyline_preview.mp4" type="video/mp4">
|
170 |
Your browser does not support the video tag.
|
171 |
</video>
|
172 |
-
<div id="videoTitleOverlay" class="video-overlay-title"></div>
|
173 |
</div>
|
174 |
-
<div class="mt-
|
175 |
<div>
|
176 |
-
<p class="text-gray-700"><strong>
|
177 |
-
<p class="text-gray-500 text-sm">Generated tutorial preview.</p>
|
178 |
</div>
|
179 |
<div>
|
180 |
-
<a href="#" id="downloadButton" class="radix-button-primary" download="tutorial.mp4"><i class="fas fa-download mr-2"></i> Download
|
181 |
</div>
|
182 |
</div>
|
183 |
</section>
|
@@ -185,37 +292,34 @@
|
|
185 |
|
186 |
<!-- Tutorial History Section -->
|
187 |
<section id="history-section" class="hidden">
|
188 |
-
<h2 class="text-
|
189 |
-
<p class="text-gray-700 mb-
|
190 |
|
191 |
<div class="tutorial-list-section">
|
192 |
<div class="tutorial-item history-item" data-video-title="Mastering JavaScript Promises">
|
193 |
-
<h3>
|
194 |
-
<p>
|
195 |
-
<p class="text-indigo-600 font-semibold">View Tutorial</p>
|
196 |
</div>
|
197 |
<div class="tutorial-item history-item" data-video-title="Introduction to Python for Data Science">
|
198 |
-
<h3>
|
199 |
-
<p>
|
200 |
-
<p class="text-indigo-600 font-semibold">View Tutorial</p>
|
201 |
</div>
|
202 |
<div class="tutorial-item history-item" data-video-title="Building a REST API with Express.js">
|
203 |
-
<h3>
|
204 |
-
<p>
|
205 |
-
<p class="text-indigo-600 font-semibold">View Tutorial</p>
|
206 |
</div>
|
207 |
</div>
|
208 |
-
<p class="mt-
|
209 |
</section>
|
210 |
|
211 |
<!-- Settings Section -->
|
212 |
<section id="settings-section" class="hidden">
|
213 |
-
<h2 class="text-
|
214 |
-
<p class="text-gray-700 mb-
|
215 |
|
216 |
<div class="settings-grid">
|
217 |
<div>
|
218 |
-
<label for="defaultDurationSetting" class="input-label">Default
|
219 |
<select id="defaultDurationSetting" class="radix-select-trigger">
|
220 |
<option value="5" class="radix-select-item">5 Minutes</option>
|
221 |
<option value="10" selected class="radix-select-item">10 Minutes</option>
|
@@ -223,11 +327,11 @@
|
|
223 |
</select>
|
224 |
</div>
|
225 |
<div>
|
226 |
-
<label for="videoQualitySetting" class="input-label">
|
227 |
<select id="videoQualitySetting" class="radix-select-trigger">
|
228 |
-
<option value="720p" selected class="radix-select-item">720p
|
229 |
-
<option value="1080p" class="radix-select-item">1080p
|
230 |
-
<option value="4k" class="radix-select-item">4K
|
231 |
</select>
|
232 |
</div>
|
233 |
<div>
|
@@ -235,12 +339,12 @@
|
|
235 |
<select id="voiceoverLanguage" class="radix-select-trigger">
|
236 |
<option value="en-US" selected class="radix-select-item">English (US)</option>
|
237 |
<option value="en-GB" class="radix-select-item">English (UK)</option>
|
238 |
-
<option value="es-ES" class="radix-select-item">Spanish
|
239 |
-
<option value="fr-FR" class="radix-select-item">French
|
240 |
</select>
|
241 |
</div>
|
242 |
<div>
|
243 |
-
<label for="outputFormat" class="input-label">
|
244 |
<select id="outputFormat" class="radix-select-trigger">
|
245 |
<option value="mp4" selected class="radix-select-item">MP4 (Recommended)</option>
|
246 |
<option value="mov" class="radix-select-item">MOV</option>
|
@@ -248,71 +352,87 @@
|
|
248 |
</select>
|
249 |
</div>
|
250 |
</div>
|
251 |
-
<div class="mt-
|
252 |
-
<button id="applySettingsButton" class="radix-button-apply"><i class="fas fa-check mr-2"></i> Apply
|
253 |
</div>
|
254 |
-
<p class="mt-
|
255 |
</section>
|
256 |
|
257 |
<!-- Help Section -->
|
258 |
<section id="help-section" class="hidden">
|
259 |
-
<h2 class="text-
|
260 |
-
<p class="text-gray-700 mb-
|
261 |
|
262 |
<div class="help-section">
|
263 |
-
<p>Welcome to Autotutorial.ai
|
264 |
<p><strong>Getting Started:</strong></p>
|
265 |
-
<ol class="list-decimal pl-
|
266 |
-
<li>Go to "Generate
|
267 |
-
<li>Enter
|
268 |
-
<li>Select
|
269 |
-
<li>(Optional) Add API keys
|
270 |
-
<li>
|
271 |
-
<li>
|
272 |
-
<li>Preview
|
273 |
</ol>
|
274 |
-
<p><strong>
|
275 |
-
<p>Access
|
276 |
<p><strong>Settings:</strong></p>
|
277 |
-
<p>Customize
|
278 |
-
<p>For
|
279 |
</div>
|
280 |
</section>
|
281 |
|
282 |
</main>
|
283 |
|
284 |
-
<!--
|
285 |
-
<
|
286 |
-
|
287 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
</div>
|
289 |
|
290 |
<script>
|
291 |
document.addEventListener('DOMContentLoaded', () => {
|
292 |
-
// Navigation functionality
|
293 |
-
const
|
294 |
const sections = document.querySelectorAll('main > section');
|
295 |
|
296 |
-
|
297 |
-
|
298 |
event.preventDefault();
|
299 |
const sectionId = this.getAttribute('data-section');
|
300 |
|
301 |
-
|
302 |
sections.forEach(sec => sec.classList.add('hidden'));
|
303 |
|
304 |
this.classList.add('active');
|
305 |
document.getElementById(`${sectionId}-section`).classList.remove('hidden');
|
306 |
if (sectionId === 'generate') {
|
307 |
-
document.getElementById('videoOutputSection').classList.add('hidden');
|
308 |
-
document.getElementById('videoOutputSection').classList.remove('opacity-0');
|
309 |
}
|
310 |
});
|
311 |
});
|
312 |
-
document.querySelector('
|
313 |
document.getElementById('generate-section').classList.remove('hidden');
|
314 |
|
315 |
-
// Tutorial History Item Click Simulation
|
316 |
const historyItems = document.querySelectorAll('.history-item');
|
317 |
const videoTitleSpan = document.getElementById('videoTitle');
|
318 |
const videoTitleOverlay = document.getElementById('videoTitleOverlay');
|
@@ -323,19 +443,14 @@
|
|
323 |
videoTitleSpan.textContent = title + " (Preview)";
|
324 |
videoTitleOverlay.textContent = title;
|
325 |
videoOutputSection.classList.remove('hidden');
|
326 |
-
videoOutputSection.classList.remove('opacity-0');
|
327 |
-
// Optionally, you could change the video source here if you had different demo videos
|
328 |
-
// document.getElementById('tutorialVideo').src = 'url_to_different_video.mp4';
|
329 |
-
// document.getElementById('tutorialVideo source').src = 'url_to_different_video.mp4';
|
330 |
-
// document.getElementById('tutorialVideo').load(); // Important to reload video source
|
331 |
-
// Show status message
|
332 |
const statusMessagesDiv = document.getElementById('statusMessages');
|
333 |
-
statusMessagesDiv.innerHTML = `<p class="text-gray-600"><i class="fas fa-video text-indigo-500 mr-1"></i> Loading
|
334 |
statusMessagesDiv.scrollTop = statusMessagesDiv.scrollHeight;
|
335 |
});
|
336 |
});
|
337 |
|
338 |
-
// Apply Settings Button Simulation
|
339 |
document.getElementById('applySettingsButton').addEventListener('click', function() {
|
340 |
const statusMessagesDiv = document.getElementById('statusMessages');
|
341 |
statusMessagesDiv.innerHTML = `<p class="text-green-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Settings Applied.</p>`;
|
@@ -345,6 +460,7 @@
|
|
345 |
|
346 |
|
347 |
document.getElementById('generateButton').addEventListener('click', function() {
|
|
|
348 |
const topic = document.getElementById('tutorialTopic').value;
|
349 |
const duration = document.getElementById('desiredDuration').value;
|
350 |
const openaiKey = document.getElementById('openaiApiKey').value;
|
@@ -363,60 +479,59 @@
|
|
363 |
statusMessagesDiv.innerHTML = '';
|
364 |
errorMessageDiv.classList.add('hidden');
|
365 |
videoOutputSection.classList.add('hidden');
|
366 |
-
videoOutputSection.classList.remove('opacity-0');
|
367 |
resetButton.classList.add('hidden');
|
368 |
generateButton.disabled = true;
|
369 |
statusSection.querySelector('h3 i').classList.remove('fa-check-circle', 'text-green-500', 'fa-times-circle', 'text-red-500');
|
370 |
statusSection.querySelector('h3 i').classList.add('fa-spinner', 'fa-spin', 'mr-2');
|
371 |
-
statusSection.querySelector('h3').innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Status
|
372 |
-
progressBar.style.transform = 'scaleX(0)';
|
373 |
-
progressBar.classList.add('active');
|
374 |
|
375 |
|
376 |
const steps = [
|
377 |
-
{ message: "Analyzing
|
378 |
-
{ message: "Generating Script
|
379 |
-
{ message: "
|
380 |
-
{ message: "Synthesizing
|
381 |
-
{ message: "Selecting
|
382 |
-
{ message: "Assembling Video
|
383 |
-
{ message: "Adding
|
384 |
-
{ message: "Rendering
|
385 |
];
|
386 |
|
387 |
let currentStepIndex = 0;
|
388 |
function processStep() {
|
389 |
if (currentStepIndex < steps.length) {
|
390 |
simulateStep(statusMessagesDiv, steps[currentStepIndex].message, steps[currentStepIndex].delay, () => {
|
391 |
-
progressBar.style.transform = `scaleX(${((currentStepIndex + 1) / steps.length)})`;
|
392 |
currentStepIndex++;
|
393 |
processStep();
|
394 |
}, true);
|
395 |
} else {
|
396 |
-
|
397 |
-
|
398 |
-
statusMessagesDiv.innerHTML = '<p class="text-green-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Tutorial generation complete!</p>';
|
399 |
statusSection.querySelector('h3 i').classList.remove('fa-spinner', 'fa-spin', 'mr-2');
|
400 |
statusSection.querySelector('h3 i').classList.add('fa-check-circle', 'text-green-500', 'mr-2');
|
401 |
-
statusSection.querySelector('h3').innerHTML = '<i class="fas fa-check-circle text-green-500 mr-2"></i> Status
|
402 |
|
403 |
-
videoTitleSpan.textContent = topic + "
|
404 |
-
videoTitleOverlay.textContent = topic
|
405 |
videoOutputSection.classList.remove('hidden');
|
406 |
-
setTimeout(() => videoOutputSection.classList.remove('opacity-0'), 50);
|
407 |
generateButton.disabled = false;
|
408 |
generateButton.classList.add('hidden');
|
409 |
resetButton.classList.remove('hidden');
|
410 |
}
|
411 |
}
|
412 |
-
processStep();
|
413 |
|
414 |
|
415 |
});
|
416 |
|
417 |
document.getElementById('resetButton').addEventListener('click', function() {
|
418 |
// ... (same resetButton event listener logic as before) ...
|
419 |
-
|
420 |
const errorMessageDiv = document.getElementById('errorMessage');
|
421 |
const generateButton = document.getElementById('generateButton');
|
422 |
const resetButton = document.getElementById('resetButton');
|
@@ -424,18 +539,18 @@
|
|
424 |
const videoOutputSection = document.getElementById('videoOutputSection');
|
425 |
const progressBar = document.getElementById('progressBar');
|
426 |
|
427 |
-
statusMessagesDiv.innerHTML = '<p class="text-gray-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Ready
|
428 |
errorMessageDiv.classList.add('hidden');
|
429 |
videoOutputSection.classList.add('hidden');
|
430 |
-
videoOutputSection.classList.remove('opacity-0');
|
431 |
generateButton.disabled = false;
|
432 |
generateButton.classList.remove('hidden');
|
433 |
resetButton.classList.add('hidden');
|
434 |
statusSection.querySelector('h3 i').classList.remove('fa-times-circle', 'text-red-500', 'fa-spinner', 'fa-spin', 'mr-2');
|
435 |
statusSection.querySelector('h3 i').classList.add('fa-check-circle', 'text-green-500', 'mr-2');
|
436 |
-
statusSection.querySelector('h3').innerHTML = '<i class="fas fa-check-circle text-green-500 mr-2"></i> Status
|
437 |
-
progressBar.classList.remove('active');
|
438 |
-
progressBar.style.transform = 'scaleX(0)';
|
439 |
|
440 |
});
|
441 |
|
@@ -445,24 +560,24 @@
|
|
445 |
const messageElement = document.createElement('p');
|
446 |
if (isSuccess) {
|
447 |
messageElement.classList.add('text-gray-600');
|
448 |
-
messageElement.innerHTML = `<i class="fas fa-spinner fa-pulse text-indigo-500 mr-1"></i> ${message}`;
|
449 |
} else {
|
450 |
messageElement.classList.add('text-red-500');
|
451 |
messageElement.innerHTML = `<i class="fas fa-times-circle text-red-500 mr-1"></i> ${message}`;
|
452 |
-
document.getElementById('errorMessage').textContent = "Error
|
453 |
document.getElementById('errorMessage').classList.remove('hidden');
|
454 |
document.getElementById('statusSection').querySelector('h3 i').classList.remove('fa-spinner', 'fa-spin', 'mr-2');
|
455 |
document.getElementById('statusSection').querySelector('h3 i').classList.add('fa-times-circle', 'text-red-500', 'mr-2');
|
456 |
-
document.getElementById('statusSection').querySelector('h3').innerHTML = '<i class="fas fa-times-circle text-red-500 mr-2"></i> Status
|
457 |
document.getElementById('generateButton').disabled = false;
|
458 |
document.getElementById('generateButton').classList.add('hidden');
|
459 |
document.getElementById('resetButton').classList.remove('hidden');
|
460 |
-
document.getElementById('progressBar').classList.remove('active');
|
461 |
}
|
462 |
statusDiv.appendChild(messageElement);
|
463 |
statusDiv.scrollTop = statusDiv.scrollHeight;
|
464 |
if (callback) callback();
|
465 |
-
}, delay + Math.random() * 500);
|
466 |
}
|
467 |
</script>
|
468 |
|
|
|
2 |
<html lang="en">
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <!-- iOS viewport settings -->
|
6 |
+
<title>Autotutorial.ai - iOS Demo</title> <!-- iOS Style Title -->
|
7 |
+
<link rel="manifest" href="manifest.json"> <!-- PWA manifest (placeholder - create manifest.json for full PWA) -->
|
8 |
+
<meta name="apple-mobile-web-app-capable" content="yes"> <!-- iOS PWA meta -->
|
9 |
+
<meta name="apple-mobile-web-app-status-bar-style" content="default"> <!-- iOS status bar style -->
|
10 |
<!-- Radix UI Styles -->
|
11 |
<link rel="stylesheet" href="https://unpkg.com/@radix-ui/[email protected]/dist/index.css">
|
12 |
<!-- Tailwind CSS CDN -->
|
13 |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
|
14 |
<!-- Font Awesome CDN -->
|
15 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
16 |
+
<!-- Inter & SF Pro Display Fonts (SF Pro Display approximation) -->
|
17 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
18 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
19 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=SF+Pro+Display:wght@400;500;600;700&display=swap" rel="stylesheet"> <!-- SF Pro Display approximation -->
|
20 |
+
|
21 |
<style>
|
22 |
+
/* iOS App Reset and Base Styles */
|
23 |
+
body {
|
24 |
+
font-family: 'Inter', sans-serif; /* Default to Inter, fallback if SF Pro Display doesn't load perfectly */
|
25 |
+
-webkit-font-smoothing: antialiased;
|
26 |
+
-moz-osx-font-smoothing: grayscale;
|
27 |
+
background-color: var(--gray-100); /* Lighter iOS-like background */
|
28 |
+
color: var(--gray-900);
|
29 |
+
margin: 0; /* Reset margins */
|
30 |
+
padding-bottom: 50px; /* Padding for bottom tab bar */
|
31 |
+
}
|
32 |
+
|
33 |
+
/* Use SF Pro Display if available, for headings and key UI text - approximation */
|
34 |
+
@font-face {
|
35 |
+
font-family: 'SF Pro Display'; /* Name to use in CSS */
|
36 |
+
src: url('https://cdn.jsdelivr.net/gh/fontsource/fontsource-sf-pro-display@latest/files/sf-pro-display-latin-400-normal.woff2') format('woff2'), /* Example CDN - consider hosting or local */
|
37 |
+
url('https://cdn.jsdelivr.net/gh/fontsource/fontsource-sf-pro-display@latest/files/sf-pro-display-latin-400-normal.woff') format('woff');
|
38 |
+
/* Add other weights as needed */
|
39 |
+
font-weight: 400;
|
40 |
+
font-style: normal;
|
41 |
+
font-display: swap; /* or optional */
|
42 |
+
}
|
43 |
+
|
44 |
+
h1, h2, h3, h4, h5, h6, .ios-title-font { font-family: 'SF Pro Display', sans-serif; } /* Apply to titles and key text */
|
45 |
+
|
46 |
+
|
47 |
+
.app-container {
|
48 |
+
max-width: 768px; /* Mobile-first max width */
|
49 |
+
margin: 0 auto; /* Center on larger screens */
|
50 |
+
background-color: var(--gray-50); /* Even lighter background, closer to iOS */
|
51 |
+
border-radius: 0px; /* No border radius for full-screen app feel */
|
52 |
+
overflow: hidden;
|
53 |
+
box-shadow: none; /* Remove shadow for flatter iOS style */
|
54 |
+
min-height: 100vh; /* Full viewport height */
|
55 |
+
display: flex;
|
56 |
+
flex-direction: column; /* Vertical flex layout */
|
57 |
+
}
|
58 |
+
|
59 |
+
header {
|
60 |
+
background-color: var(--gray-50); /* White header background */
|
61 |
+
padding: 2rem 1.5rem 1rem 1.5rem; /* Adjusted header padding - less top, more bottom */
|
62 |
+
color: var(--gray-900);
|
63 |
+
border-bottom: 1px solid var(--gray-200); /* iOS-style separator */
|
64 |
+
}
|
65 |
+
|
66 |
+
header h1 {
|
67 |
+
font-size: 2.25rem; /* Large iOS title size */
|
68 |
+
font-weight: 700;
|
69 |
+
letter-spacing: -0.02em;
|
70 |
+
margin-bottom: 0.25rem; /* Reduced bottom margin */
|
71 |
+
text-align: left; /* Left-align iOS style */
|
72 |
+
}
|
73 |
+
|
74 |
+
header p {
|
75 |
+
font-size: 1.1rem; /* Slightly smaller subtitle */
|
76 |
+
color: var(--gray-600); /* Muted subtitle color */
|
77 |
+
font-weight: 400;
|
78 |
+
}
|
79 |
+
|
80 |
+
main {
|
81 |
+
padding: 1.5rem; /* Reduced main padding for mobile */
|
82 |
+
flex-grow: 1; /* Main content takes up remaining space */
|
83 |
+
}
|
84 |
+
|
85 |
+
section {
|
86 |
+
margin-bottom: 2rem; /* Reduced section margin */
|
87 |
+
}
|
88 |
+
|
89 |
+
section h2 {
|
90 |
+
font-size: 1.7rem; /* Section title size */
|
91 |
+
font-weight: 600;
|
92 |
+
color: var(--gray-800);
|
93 |
+
margin-bottom: 1.25rem; /* Reduced section title margin */
|
94 |
+
letter-spacing: -0.015em;
|
95 |
+
text-align: left; /* Left-align section titles */
|
96 |
+
}
|
97 |
+
|
98 |
+
section h3 {
|
99 |
+
font-size: 1.3rem; /* Subtitle size */
|
100 |
+
font-weight: 500;
|
101 |
+
color: var(--gray-700);
|
102 |
+
margin-bottom: 0.8rem; /* Reduced subtitle margin */
|
103 |
+
letter-spacing: -0.01em;
|
104 |
+
text-align: left; /* Left-align subtitles */
|
105 |
+
}
|
106 |
+
|
107 |
+
.input-label { @apply block text-gray-700 text-sm font-semibold mb-1; } /* Smaller label margin */
|
108 |
+
.status-box { @apply border rounded-md p-4 mb-3 bg-gray-50 border-gray-200 relative overflow-hidden; } /* Reduced status box margin */
|
109 |
+
.error-text { @apply text-red-500 mt-1 text-sm; } /* Smaller error text margin */
|
110 |
+
.video-player-container { @apply rounded-md overflow-hidden shadow-md; } /* Keep shadow for video player */
|
111 |
+
.video-overlay-title { position: absolute; bottom: 15px; left: 15px; color: white; font-size: 1.5rem; font-weight: 600; text-shadow: 1px 1px 2px rgba(0,0,0,0.6); } /* Smaller video overlay title */
|
112 |
+
|
113 |
+
footer {
|
114 |
+
background-color: var(--gray-50); /* White footer */
|
115 |
+
border-top: 1px solid var(--gray-200); /* iOS footer separator */
|
116 |
+
padding: 1.25rem 1.5rem; /* Reduced footer padding */
|
117 |
+
text-center text-gray-500 text-sm;
|
118 |
+
}
|
119 |
+
|
120 |
footer a { color: var(--gray-600); text-decoration: none; }
|
121 |
footer a:hover { text-decoration: underline; }
|
122 |
+
|
123 |
+
.tutorial-item { @apply bg-white p-3.5 rounded-md shadow-sm border border-gray-100 hover:shadow-md transition-shadow duration-200 cursor-pointer mb-2; } /* Reduced tutorial item padding and margin */
|
124 |
+
.tutorial-item h3 { @apply font-semibold text-gray-800 mb-1; font-size: 1.1rem; } /* Smaller tutorial item title */
|
125 |
.tutorial-item p { @apply text-gray-600 text-sm; }
|
126 |
+
.tutorial-list-section { display: flex; flex-direction: column; gap: 0.75rem; } /* Vertical tutorial list */
|
127 |
+
.settings-grid { display: grid; grid-template-columns: 1fr; gap: 1.75rem; } /* Single column settings on mobile */
|
128 |
+
@media (min-width: 768px) { /* Medium screens and up */
|
129 |
+
.settings-grid { grid-template-columns: 1fr 1fr; } /* Two-column settings on larger screens */
|
130 |
+
}
|
131 |
+
.help-section p { line-height: 1.6; margin-bottom: 1rem; }
|
132 |
|
133 |
+
/* Radix UI Button Styling - iOS Style Refinements */
|
134 |
.radix-button {
|
135 |
+
@apply inline-flex items-center justify-center rounded-md px-4 py-2 font-semibold text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors duration-200; /* Reduced button padding */
|
136 |
+
-webkit-tap-highlight-color: transparent; /* Remove tap highlight on iOS */
|
137 |
}
|
138 |
+
.radix-button-primary { @apply radix-button bg-indigo-600 hover:bg-indigo-700 active:bg-indigo-800 text-white shadow-sm hover:shadow-md; }
|
139 |
.radix-button-primary:disabled { @apply bg-indigo-300 cursor-not-allowed hover:bg-indigo-300 focus:ring-0 shadow-none; }
|
140 |
+
.radix-button-secondary { @apply radix-button bg-gray-200 hover:bg-gray-300 active:bg-gray-400 text-gray-700 shadow-sm hover:shadow-md; }
|
141 |
+
.radix-button-apply { @apply radix-button bg-green-500 hover:bg-green-600 active:bg-green-700 text-white shadow-sm hover:shadow-md; }
|
142 |
|
143 |
+
/* Radix UI Input Styling - iOS Style Refinements */
|
144 |
.radix-input {
|
145 |
+
@apply appearance-none border rounded-md w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-shadow duration-200; /* Reduced input padding */
|
146 |
border-color: var(--gray-300);
|
147 |
+
background-color: var(--gray-50); /* Light gray input background */
|
148 |
color: var(--gray-900);
|
149 |
+
box-shadow: inset 0 0 0 1px var(--gray-300); /* iOS-like border shadow */
|
150 |
}
|
151 |
+
.radix-input:focus { @apply shadow-none border-indigo-500; box-shadow: inset 0 0 0 2px var(--indigo-500); } /* iOS-like focus */
|
152 |
|
153 |
+
/* Radix UI Select Styling - iOS Style - Needs more JS for true iOS Picker */
|
154 |
.radix-select-trigger {
|
155 |
@apply radix-button radix-button-secondary w-full justify-between;
|
156 |
+
border-radius: 8px; /* More rounded select */
|
157 |
+
padding-left: 1rem; padding-right: 1rem; /* Adjust select padding */
|
158 |
}
|
159 |
.radix-select-content {
|
160 |
@apply rounded-md shadow-md bg-white border border-gray-200 overflow-hidden;
|
|
|
164 |
}
|
165 |
.radix-select-item[data-highlighted] { @apply bg-indigo-50 text-indigo-900; }
|
166 |
|
167 |
+
/* Progress Bar - iOS Style - Thinner */
|
168 |
+
.status-progress-bar { position: absolute; bottom: 0; left: 0; right: 0; height: 3px; background-color: var(--indigo-500); transform-origin: 0% 0%; transform: scaleX(0); transition: transform 0.4s ease-out; }
|
169 |
.status-progress-bar.active { transform: scaleX(1); }
|
170 |
|
171 |
/* Transitions */
|
172 |
.transition-opacity { transition-property: opacity; transition-timing-function: ease-out; transition-duration: 200ms; }
|
173 |
|
174 |
+
/* Bottom Tab Bar Navigation - iOS Style */
|
175 |
+
.bottom-tab-bar {
|
176 |
+
position: fixed; bottom: 0; left: 0; right: 0;
|
177 |
+
background-color: var(--gray-50); /* White tab bar background */
|
178 |
+
border-top: 1px solid var(--gray-200); /* iOS tab bar border */
|
179 |
+
padding-top: 0.5rem; padding-bottom: calc(env(safe-area-inset-bottom) + 0.5rem); /* Safe area padding for iOS */
|
180 |
+
display: flex; justify-content: space-around;
|
181 |
+
z-index: 100; /* Ensure it's on top */
|
182 |
+
}
|
183 |
+
|
184 |
+
.bottom-tab-button {
|
185 |
+
display: flex; flex-direction: column; align-items: center;
|
186 |
+
color: var(--gray-500); /* Inactive tab color */
|
187 |
+
text-decoration: none;
|
188 |
+
font-size: 0.85rem; /* Smaller tab label */
|
189 |
+
padding: 0.5rem 1rem; border-radius: 8px; /* Tab button padding and rounded corners */
|
190 |
+
-webkit-tap-highlight-color: transparent; /* Remove tap highlight on iOS */
|
191 |
+
}
|
192 |
+
|
193 |
+
.bottom-tab-button.active, .bottom-tab-button:active, .bottom-tab-button:hover {
|
194 |
+
color: var(--indigo-600); /* Active/tapped tab color */
|
195 |
+
background-color: var(--gray-100); /* Slightly highlight active tab */
|
196 |
+
}
|
197 |
+
|
198 |
+
.bottom-tab-icon {
|
199 |
+
font-size: 1.3rem; /* Tab icon size */
|
200 |
+
margin-bottom: 0.25rem;
|
201 |
+
}
|
202 |
+
|
203 |
+
/* Scrollbar - minimal for mobile */
|
204 |
+
::-webkit-scrollbar { width: 4px; height: 4px; }
|
205 |
+
::-webkit-scrollbar-track { background: transparent; } /* Transparent track */
|
206 |
+
::-webkit-scrollbar-thumb { background: var(--gray-400); border-radius: 2px; } /* Thinner, lighter thumb */
|
207 |
+
::-webkit-scrollbar-thumb:hover { background: var(--gray-500); }
|
208 |
+
* { scrollbar-width: thin; scrollbar-color: var(--gray-400) transparent; } /* Firefox scrollbar */
|
209 |
+
|
210 |
|
211 |
</style>
|
212 |
</head>
|
213 |
+
<body class="bg-gray-100"> <!-- Body background for overscroll effect -->
|
214 |
<div class="app-container">
|
215 |
+
<!-- Header Section (iOS Style - Large Title) -->
|
216 |
<header>
|
217 |
+
<h1 class="ios-title-font">Autotutorial.ai</h1>
|
218 |
+
<p>Generate Video Tutorials</p>
|
219 |
</header>
|
220 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
<!-- Main Content Area -->
|
222 |
<main>
|
223 |
<!-- Generate Tutorial Section -->
|
224 |
<section id="generate-section">
|
225 |
+
<h2 class="text-xl">New Tutorial</h2>
|
226 |
+
<p class="text-gray-700 mb-6">Set topic and duration to begin.</p>
|
227 |
|
228 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
|
229 |
<div>
|
230 |
+
<label for="tutorialTopic" class="input-label">Topic <span class="text-gray-500">(e.g., "React Forms")</span></label>
|
231 |
+
<input type="text" id="tutorialTopic" class="radix-input" placeholder="Enter topic" value="Vue.js Teleport Feature" required>
|
232 |
</div>
|
233 |
<div>
|
234 |
+
<label for="desiredDuration" class="input-label">Duration (Minutes)</label>
|
235 |
<select id="desiredDuration" class="radix-select-trigger">
|
236 |
<option value="5" class="radix-select-item">5 Minutes</option>
|
237 |
<option value="10" selected class="radix-select-item">10 Minutes</option>
|
|
|
241 |
</div>
|
242 |
</div>
|
243 |
|
244 |
+
<section class="mb-5">
|
245 |
+
<h3 class="text-lg font-semibold mb-3 text-gray-800">API Keys <span class="text-gray-500">(Optional)</span></h3>
|
246 |
<div class="settings-grid">
|
247 |
<div>
|
248 |
+
<label for="openaiApiKey" class="input-label">OpenAI Key <span class="text-gray-500">(Script AI)</span></label>
|
249 |
+
<input type="text" id="openaiApiKey" class="radix-input" placeholder="OpenAI API Key (Optional)" value="sk-DEMO_OPENAI_KEY">
|
250 |
</div>
|
251 |
<div>
|
252 |
+
<label for="videoGenApiKey" class="input-label">Video Key <span class="text-gray-500">(Video AI)</span></label>
|
253 |
+
<input type="text" id="videoGenApiKey" class="radix-input" placeholder="Video API Key (Optional)" value="vg-DEMO_VIDEO_GEN_KEY">
|
254 |
</div>
|
255 |
</div>
|
256 |
+
<p class="text-sm text-gray-500 mt-2">For enhanced AI features.</p>
|
257 |
</section>
|
258 |
|
259 |
+
<section class="mb-5 flex justify-start gap-3">
|
260 |
+
<button id="generateButton" class="radix-button-primary"><i class="fas fa-magic mr-2"></i> Generate</button>
|
261 |
<button id="resetButton" class="radix-button-secondary hidden"><i class="fas fa-redo mr-2"></i> Reset</button>
|
262 |
</section>
|
263 |
|
264 |
<section id="statusSection" class="status-box">
|
265 |
+
<h3 class="font-semibold text-gray-700 mb-2"><i class="fas fa-info-circle mr-2"></i> Status</h3>
|
266 |
<div id="statusMessages">
|
267 |
+
<p class="text-gray-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Ready. Tap "Generate".</p>
|
268 |
</div>
|
269 |
<p id="errorMessage" class="error-text hidden"></p>
|
270 |
+
<div id="progressBar" class="status-progress-bar"></div>
|
271 |
</section>
|
272 |
|
273 |
<section id="videoOutputSection" class="hidden transition-opacity opacity-0 duration-200">
|
274 |
+
<h2 class="text-lg font-semibold mb-3 text-gray-800"><i class="fas fa-film mr-2"></i> Preview</h2>
|
275 |
<div class="video-player-container">
|
276 |
<video id="tutorialVideo" controls width="100%">
|
277 |
+
<source src="https://joy.videvo.net/videvo_files/video/free/2016-06/small_watermarked/Drone_Flying_Above_City_Skyline_preview.mp4" type="video/mp4">
|
278 |
Your browser does not support the video tag.
|
279 |
</video>
|
280 |
+
<div id="videoTitleOverlay" class="video-overlay-title"></div>
|
281 |
</div>
|
282 |
+
<div class="mt-3 flex justify-between items-center">
|
283 |
<div>
|
284 |
+
<p class="text-gray-700 text-sm"><strong>Title:</strong> <span id="videoTitle"></span></p>
|
|
|
285 |
</div>
|
286 |
<div>
|
287 |
+
<a href="#" id="downloadButton" class="radix-button-primary" download="tutorial.mp4"><i class="fas fa-download mr-2"></i> Download</a>
|
288 |
</div>
|
289 |
</div>
|
290 |
</section>
|
|
|
292 |
|
293 |
<!-- Tutorial History Section -->
|
294 |
<section id="history-section" class="hidden">
|
295 |
+
<h2 class="text-xl">History</h2>
|
296 |
+
<p class="text-gray-700 mb-6">Past tutorials.</p>
|
297 |
|
298 |
<div class="tutorial-list-section">
|
299 |
<div class="tutorial-item history-item" data-video-title="Mastering JavaScript Promises">
|
300 |
+
<h3>JavaScript Promises</h3>
|
301 |
+
<p>Oct 26, 2023</p>
|
|
|
302 |
</div>
|
303 |
<div class="tutorial-item history-item" data-video-title="Introduction to Python for Data Science">
|
304 |
+
<h3>Python for Data Science</h3>
|
305 |
+
<p>Oct 25, 2023</p>
|
|
|
306 |
</div>
|
307 |
<div class="tutorial-item history-item" data-video-title="Building a REST API with Express.js">
|
308 |
+
<h3>REST API with Express.js</h3>
|
309 |
+
<p>Oct 20, 2023</p>
|
|
|
310 |
</div>
|
311 |
</div>
|
312 |
+
<p class="mt-3 text-sm text-gray-500">Demo history.</p>
|
313 |
</section>
|
314 |
|
315 |
<!-- Settings Section -->
|
316 |
<section id="settings-section" class="hidden">
|
317 |
+
<h2 class="text-xl">Settings</h2>
|
318 |
+
<p class="text-gray-700 mb-6">App preferences.</p>
|
319 |
|
320 |
<div class="settings-grid">
|
321 |
<div>
|
322 |
+
<label for="defaultDurationSetting" class="input-label">Default Duration</label>
|
323 |
<select id="defaultDurationSetting" class="radix-select-trigger">
|
324 |
<option value="5" class="radix-select-item">5 Minutes</option>
|
325 |
<option value="10" selected class="radix-select-item">10 Minutes</option>
|
|
|
327 |
</select>
|
328 |
</div>
|
329 |
<div>
|
330 |
+
<label for="videoQualitySetting" class="input-label">Video Quality</label>
|
331 |
<select id="videoQualitySetting" class="radix-select-trigger">
|
332 |
+
<option value="720p" selected class="radix-select-item">720p HD</option>
|
333 |
+
<option value="1080p" class="radix-select-item">1080p Full HD</option>
|
334 |
+
<option value="4k" class="radix-select-item">4K Ultra HD (Premium)</option>
|
335 |
</select>
|
336 |
</div>
|
337 |
<div>
|
|
|
339 |
<select id="voiceoverLanguage" class="radix-select-trigger">
|
340 |
<option value="en-US" selected class="radix-select-item">English (US)</option>
|
341 |
<option value="en-GB" class="radix-select-item">English (UK)</option>
|
342 |
+
<option value="es-ES" class="radix-select-item">Spanish</option>
|
343 |
+
<option value="fr-FR" class="radix-select-item">French</option>
|
344 |
</select>
|
345 |
</div>
|
346 |
<div>
|
347 |
+
<label for="outputFormat" class="input-label">Output Format</label>
|
348 |
<select id="outputFormat" class="radix-select-trigger">
|
349 |
<option value="mp4" selected class="radix-select-item">MP4 (Recommended)</option>
|
350 |
<option value="mov" class="radix-select-item">MOV</option>
|
|
|
352 |
</select>
|
353 |
</div>
|
354 |
</div>
|
355 |
+
<div class="mt-4 flex justify-start">
|
356 |
+
<button id="applySettingsButton" class="radix-button-apply"><i class="fas fa-check mr-2"></i> Apply</button>
|
357 |
</div>
|
358 |
+
<p class="mt-3 text-sm text-gray-500">Demo settings.</p>
|
359 |
</section>
|
360 |
|
361 |
<!-- Help Section -->
|
362 |
<section id="help-section" class="hidden">
|
363 |
+
<h2 class="text-xl">Help</h2>
|
364 |
+
<p class="text-gray-700 mb-6">App guidance.</p>
|
365 |
|
366 |
<div class="help-section">
|
367 |
+
<p>Welcome to Autotutorial.ai! Generate videos easily.</p>
|
368 |
<p><strong>Getting Started:</strong></p>
|
369 |
+
<ol class="list-decimal pl-5 mb-4">
|
370 |
+
<li>Go to "Generate" tab.</li>
|
371 |
+
<li>Enter tutorial topic.</li>
|
372 |
+
<li>Select duration.</li>
|
373 |
+
<li>(Optional) Add API keys.</li>
|
374 |
+
<li>Tap "Generate".</li>
|
375 |
+
<li>See "Status" for progress.</li>
|
376 |
+
<li>Preview in "Preview" section.</li>
|
377 |
</ol>
|
378 |
+
<p><strong>History:</strong></p>
|
379 |
+
<p>Access past tutorials in "History" (demo).</p>
|
380 |
<p><strong>Settings:</strong></p>
|
381 |
+
<p>Customize preferences in "Settings" (demo).</p>
|
382 |
+
<p>For support, contact <a href="mailto:[email protected]">[email protected]</a>.</p>
|
383 |
</div>
|
384 |
</section>
|
385 |
|
386 |
</main>
|
387 |
|
388 |
+
<!-- Bottom Tab Bar Navigation -->
|
389 |
+
<nav class="bottom-tab-bar">
|
390 |
+
<a href="#generate" class="bottom-tab-button active" data-section="generate">
|
391 |
+
<i class="fas fa-magic bottom-tab-icon"></i>
|
392 |
+
Generate
|
393 |
+
</a>
|
394 |
+
<a href="#history" class="bottom-tab-button" data-section="history">
|
395 |
+
<i class="fas fa-history bottom-tab-icon"></i>
|
396 |
+
History
|
397 |
+
</a>
|
398 |
+
<a href="#settings" class="bottom-tab-button" data-section="settings">
|
399 |
+
<i class="fas fa-cog bottom-tab-icon"></i>
|
400 |
+
Settings
|
401 |
+
</a>
|
402 |
+
<a href="#help" class="bottom-tab-button" data-section="help">
|
403 |
+
<i class="fas fa-question-circle bottom-tab-icon"></i>
|
404 |
+
Help
|
405 |
+
</a>
|
406 |
+
</nav>
|
407 |
+
|
408 |
</div>
|
409 |
|
410 |
<script>
|
411 |
document.addEventListener('DOMContentLoaded', () => {
|
412 |
+
// Bottom Tab Navigation functionality
|
413 |
+
const tabButtons = document.querySelectorAll('.bottom-tab-button');
|
414 |
const sections = document.querySelectorAll('main > section');
|
415 |
|
416 |
+
tabButtons.forEach(button => {
|
417 |
+
button.addEventListener('click', function(event) {
|
418 |
event.preventDefault();
|
419 |
const sectionId = this.getAttribute('data-section');
|
420 |
|
421 |
+
tabButtons.forEach(btn => btn.classList.remove('active'));
|
422 |
sections.forEach(sec => sec.classList.add('hidden'));
|
423 |
|
424 |
this.classList.add('active');
|
425 |
document.getElementById(`${sectionId}-section`).classList.remove('hidden');
|
426 |
if (sectionId === 'generate') {
|
427 |
+
document.getElementById('videoOutputSection').classList.add('hidden');
|
428 |
+
document.getElementById('videoOutputSection').classList.remove('opacity-0');
|
429 |
}
|
430 |
});
|
431 |
});
|
432 |
+
document.querySelector('.bottom-tab-button[data-section="generate"]').classList.add('active');
|
433 |
document.getElementById('generate-section').classList.remove('hidden');
|
434 |
|
435 |
+
// Tutorial History Item Click Simulation (same as before)
|
436 |
const historyItems = document.querySelectorAll('.history-item');
|
437 |
const videoTitleSpan = document.getElementById('videoTitle');
|
438 |
const videoTitleOverlay = document.getElementById('videoTitleOverlay');
|
|
|
443 |
videoTitleSpan.textContent = title + " (Preview)";
|
444 |
videoTitleOverlay.textContent = title;
|
445 |
videoOutputSection.classList.remove('hidden');
|
446 |
+
videoOutputSection.classList.remove('opacity-0');
|
|
|
|
|
|
|
|
|
|
|
447 |
const statusMessagesDiv = document.getElementById('statusMessages');
|
448 |
+
statusMessagesDiv.innerHTML = `<p class="text-gray-600"><i class="fas fa-video text-indigo-500 mr-1"></i> Loading: ${title}</p>`;
|
449 |
statusMessagesDiv.scrollTop = statusMessagesDiv.scrollHeight;
|
450 |
});
|
451 |
});
|
452 |
|
453 |
+
// Apply Settings Button Simulation (same as before)
|
454 |
document.getElementById('applySettingsButton').addEventListener('click', function() {
|
455 |
const statusMessagesDiv = document.getElementById('statusMessages');
|
456 |
statusMessagesDiv.innerHTML = `<p class="text-green-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Settings Applied.</p>`;
|
|
|
460 |
|
461 |
|
462 |
document.getElementById('generateButton').addEventListener('click', function() {
|
463 |
+
// ... (same generateButton event listener logic as before) ...
|
464 |
const topic = document.getElementById('tutorialTopic').value;
|
465 |
const duration = document.getElementById('desiredDuration').value;
|
466 |
const openaiKey = document.getElementById('openaiApiKey').value;
|
|
|
479 |
statusMessagesDiv.innerHTML = '';
|
480 |
errorMessageDiv.classList.add('hidden');
|
481 |
videoOutputSection.classList.add('hidden');
|
482 |
+
videoOutputSection.classList.remove('opacity-0');
|
483 |
resetButton.classList.add('hidden');
|
484 |
generateButton.disabled = true;
|
485 |
statusSection.querySelector('h3 i').classList.remove('fa-check-circle', 'text-green-500', 'fa-times-circle', 'text-red-500');
|
486 |
statusSection.querySelector('h3 i').classList.add('fa-spinner', 'fa-spin', 'mr-2');
|
487 |
+
statusSection.querySelector('h3').innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Status';
|
488 |
+
progressBar.style.transform = 'scaleX(0)';
|
489 |
+
progressBar.classList.add('active');
|
490 |
|
491 |
|
492 |
const steps = [
|
493 |
+
{ message: "Analyzing Topic...", delay: 800 },
|
494 |
+
{ message: "Generating Script...", delay: 1500 },
|
495 |
+
{ message: "Creating Voiceover...", delay: 2200 },
|
496 |
+
{ message: "Synthesizing Scenes...", delay: 2000 },
|
497 |
+
{ message: "Selecting Visuals...", delay: 1800 },
|
498 |
+
{ message: "Assembling Video...", delay: 2500 },
|
499 |
+
{ message: "Adding Effects...", delay: 1500 },
|
500 |
+
{ message: "Rendering Video...", delay: 3000 }
|
501 |
];
|
502 |
|
503 |
let currentStepIndex = 0;
|
504 |
function processStep() {
|
505 |
if (currentStepIndex < steps.length) {
|
506 |
simulateStep(statusMessagesDiv, steps[currentStepIndex].message, steps[currentStepIndex].delay, () => {
|
507 |
+
progressBar.style.transform = `scaleX(${((currentStepIndex + 1) / steps.length)})`;
|
508 |
currentStepIndex++;
|
509 |
processStep();
|
510 |
}, true);
|
511 |
} else {
|
512 |
+
progressBar.classList.remove('active');
|
513 |
+
statusMessagesDiv.innerHTML = '<p class="text-green-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Generation Complete!</p>';
|
|
|
514 |
statusSection.querySelector('h3 i').classList.remove('fa-spinner', 'fa-spin', 'mr-2');
|
515 |
statusSection.querySelector('h3 i').classList.add('fa-check-circle', 'text-green-500', 'mr-2');
|
516 |
+
statusSection.querySelector('h3').innerHTML = '<i class="fas fa-check-circle text-green-500 mr-2"></i> Status';
|
517 |
|
518 |
+
videoTitleSpan.textContent = topic + " (" + duration + "min Preview)";
|
519 |
+
videoTitleOverlay.textContent = topic;
|
520 |
videoOutputSection.classList.remove('hidden');
|
521 |
+
setTimeout(() => videoOutputSection.classList.remove('opacity-0'), 50);
|
522 |
generateButton.disabled = false;
|
523 |
generateButton.classList.add('hidden');
|
524 |
resetButton.classList.remove('hidden');
|
525 |
}
|
526 |
}
|
527 |
+
processStep();
|
528 |
|
529 |
|
530 |
});
|
531 |
|
532 |
document.getElementById('resetButton').addEventListener('click', function() {
|
533 |
// ... (same resetButton event listener logic as before) ...
|
534 |
+
const statusMessagesDiv = document.getElementById('statusMessages');
|
535 |
const errorMessageDiv = document.getElementById('errorMessage');
|
536 |
const generateButton = document.getElementById('generateButton');
|
537 |
const resetButton = document.getElementById('resetButton');
|
|
|
539 |
const videoOutputSection = document.getElementById('videoOutputSection');
|
540 |
const progressBar = document.getElementById('progressBar');
|
541 |
|
542 |
+
statusMessagesDiv.innerHTML = '<p class="text-gray-600"><i class="fas fa-check-circle text-green-500 mr-1"></i> Ready. Tap "Generate".</p>';
|
543 |
errorMessageDiv.classList.add('hidden');
|
544 |
videoOutputSection.classList.add('hidden');
|
545 |
+
videoOutputSection.classList.remove('opacity-0');
|
546 |
generateButton.disabled = false;
|
547 |
generateButton.classList.remove('hidden');
|
548 |
resetButton.classList.add('hidden');
|
549 |
statusSection.querySelector('h3 i').classList.remove('fa-times-circle', 'text-red-500', 'fa-spinner', 'fa-spin', 'mr-2');
|
550 |
statusSection.querySelector('h3 i').classList.add('fa-check-circle', 'text-green-500', 'mr-2');
|
551 |
+
statusSection.querySelector('h3').innerHTML = '<i class="fas fa-check-circle text-green-500 mr-2"></i> Status';
|
552 |
+
progressBar.classList.remove('active');
|
553 |
+
progressBar.style.transform = 'scaleX(0)';
|
554 |
|
555 |
});
|
556 |
|
|
|
560 |
const messageElement = document.createElement('p');
|
561 |
if (isSuccess) {
|
562 |
messageElement.classList.add('text-gray-600');
|
563 |
+
messageElement.innerHTML = `<i class="fas fa-spinner fa-pulse text-indigo-500 mr-1"></i> ${message}`;
|
564 |
} else {
|
565 |
messageElement.classList.add('text-red-500');
|
566 |
messageElement.innerHTML = `<i class="fas fa-times-circle text-red-500 mr-1"></i> ${message}`;
|
567 |
+
document.getElementById('errorMessage').textContent = "Error generating tutorial. Check API keys.";
|
568 |
document.getElementById('errorMessage').classList.remove('hidden');
|
569 |
document.getElementById('statusSection').querySelector('h3 i').classList.remove('fa-spinner', 'fa-spin', 'mr-2');
|
570 |
document.getElementById('statusSection').querySelector('h3 i').classList.add('fa-times-circle', 'text-red-500', 'mr-2');
|
571 |
+
document.getElementById('statusSection').querySelector('h3').innerHTML = '<i class="fas fa-times-circle text-red-500 mr-2"></i> Status';
|
572 |
document.getElementById('generateButton').disabled = false;
|
573 |
document.getElementById('generateButton').classList.add('hidden');
|
574 |
document.getElementById('resetButton').classList.remove('hidden');
|
575 |
+
document.getElementById('progressBar').classList.remove('active');
|
576 |
}
|
577 |
statusDiv.appendChild(messageElement);
|
578 |
statusDiv.scrollTop = statusDiv.scrollHeight;
|
579 |
if (callback) callback();
|
580 |
+
}, delay + Math.random() * 500);
|
581 |
}
|
582 |
</script>
|
583 |
|