Spaces:
Running
Running
Update index.html
Browse files- index.html +403 -372
index.html
CHANGED
@@ -3,435 +3,466 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
6 |
-
<title>Autotutorial.ai
|
7 |
-
<link rel="manifest" href="manifest.json">
|
8 |
<meta name="apple-mobile-web-app-capable" content="yes">
|
9 |
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
10 |
|
11 |
<!-- Ionic CSS -->
|
12 |
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@
|
13 |
|
14 |
-
<!--
|
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 |
-
|
17 |
-
<!-- Inter Font from Google Fonts -->
|
18 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
19 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
20 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
21 |
-
|
22 |
-
<style>
|
23 |
:root {
|
24 |
--ion-font-family: 'Inter', sans-serif;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
}
|
|
|
|
|
26 |
body {
|
27 |
font-family: var(--ion-font-family);
|
|
|
|
|
28 |
}
|
29 |
-
|
30 |
-
|
|
|
31 |
overflow: hidden;
|
32 |
-
|
33 |
-
|
34 |
}
|
35 |
-
|
|
|
36 |
position: absolute;
|
37 |
bottom: 16px;
|
38 |
left: 16px;
|
39 |
color: white;
|
40 |
-
font-size: 1.
|
41 |
font-weight: 600;
|
42 |
-
text-shadow:
|
43 |
-
}
|
44 |
-
.status-box {
|
45 |
-
border-radius: 8px;
|
46 |
-
border: 1px solid var(--ion-color-light);
|
47 |
-
background-color: var(--ion-color-light-contrast);
|
48 |
-
padding: 16px;
|
49 |
-
margin-bottom: 16px;
|
50 |
}
|
51 |
-
.
|
52 |
-
|
53 |
}
|
|
|
54 |
</style>
|
55 |
</head>
|
56 |
<body>
|
57 |
-
|
58 |
<ion-app>
|
59 |
-
|
60 |
-
|
61 |
-
<ion-toolbar>
|
62 |
<ion-title>Autotutorial.ai</ion-title>
|
|
|
|
|
|
|
|
|
|
|
63 |
</ion-toolbar>
|
64 |
</ion-header>
|
65 |
|
66 |
-
<ion-content
|
67 |
-
|
68 |
-
<
|
69 |
-
|
70 |
-
|
71 |
-
<ion-card-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
<ion-
|
77 |
-
<ion-
|
78 |
-
<ion-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
<ion-button id="resetButton" expand="block" color="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
</div>
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
<
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
<
|
140 |
-
</
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
<
|
147 |
-
<ion-card
|
148 |
-
<ion-card-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
<ion-
|
153 |
-
<ion-
|
154 |
-
<ion-
|
155 |
-
<
|
156 |
-
<
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
<ion-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
</
|
217 |
-
<
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
<ion-card-header>
|
226 |
-
<ion-card-title><ion-icon icon="help-circle" style="margin-right: 8px;"></ion-icon>Help & Support</ion-card-title>
|
227 |
-
<ion-card-subtitle>Guidance and resources</ion-card-subtitle>
|
228 |
-
</ion-card-header>
|
229 |
-
<ion-card-content>
|
230 |
-
<p>Welcome to Autotutorial.ai! Generate tutorials with ease.</p>
|
231 |
-
<p><strong>Getting Started:</strong></p>
|
232 |
-
<ol>
|
233 |
-
<li>Go to "Generate Tutorial" tab.</li>
|
234 |
-
<li>Enter your tutorial topic.</li>
|
235 |
-
<li>Select desired duration.</li>
|
236 |
-
<li>(Optional) Add API keys for enhanced features.</li>
|
237 |
-
<li>Tap "Generate Tutorial" to start.</li>
|
238 |
-
<li>Monitor "Status Updates" for progress.</li>
|
239 |
-
<li>Preview tutorial in "Tutorial Preview" section.</li>
|
240 |
-
</ol>
|
241 |
-
<p><strong>Tutorial History:</strong></p>
|
242 |
-
<p>Access and manage past tutorials in "Tutorial History" (demo feature).</p>
|
243 |
-
<p><strong>Settings:</strong></p>
|
244 |
-
<p>Customize default preferences in "Settings" (basic demo settings).</p>
|
245 |
-
<p>For further assistance, contact support at <a href="mailto:[email protected]">[email protected]</a>.</p>
|
246 |
-
</ion-card-content>
|
247 |
-
</ion-card>
|
248 |
|
249 |
</ion-content>
|
250 |
|
251 |
-
<ion-
|
252 |
-
<ion-
|
253 |
-
<ion-
|
254 |
-
<ion-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
</ion-
|
269 |
-
</ion-
|
270 |
-
</ion-
|
271 |
|
272 |
</ion-app>
|
273 |
|
274 |
-
<!-- Ionic
|
275 |
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@
|
276 |
-
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@
|
277 |
|
278 |
<script>
|
279 |
-
document.addEventListener('DOMContentLoaded', () => {
|
280 |
-
// Tab Navigation
|
281 |
-
const tabButtons = document.querySelectorAll('.tab-button-link');
|
282 |
-
const sections = ['generate-section', 'history-section', 'settings-section', 'help-section'];
|
283 |
-
|
284 |
-
tabButtons.forEach(button => {
|
285 |
-
button.addEventListener('click', function(event) {
|
286 |
-
event.preventDefault();
|
287 |
-
const sectionId = this.getAttribute('data-section') + '-section';
|
288 |
-
|
289 |
-
tabButtons.forEach(btn => btn.classList.remove('active'));
|
290 |
-
sections.forEach(sec => document.getElementById(sec).classList.add('ion-hide'));
|
291 |
-
|
292 |
-
this.classList.add('active');
|
293 |
-
document.getElementById(sectionId).classList.remove('ion-hide');
|
294 |
-
if (sectionId === 'generate-section') {
|
295 |
-
document.getElementById('videoOutputSection').classList.add('ion-hide');
|
296 |
-
document.getElementById('videoOutputSection').classList.remove('opacity-0');
|
297 |
-
}
|
298 |
-
});
|
299 |
-
});
|
300 |
-
document.querySelector('.tab-button-link[data-section="generate"]').classList.add('active');
|
301 |
-
sections.filter(sec => sec !== 'generate-section').forEach(sec => document.getElementById(sec).classList.add('ion-hide'));
|
302 |
|
|
|
|
|
|
|
|
|
|
|
303 |
|
304 |
-
//
|
305 |
-
|
306 |
-
const videoTitleSpan = document.getElementById('videoTitle');
|
307 |
-
const videoTitleOverlay = document.getElementById('videoTitleOverlay');
|
308 |
-
const videoOutputSection = document.getElementById('videoOutputSection');
|
309 |
-
historyItems.forEach(item => {
|
310 |
-
item.addEventListener('click', function() {
|
311 |
-
const title = this.getAttribute('data-video-title');
|
312 |
-
videoTitleSpan.textContent = title + " (Preview)";
|
313 |
-
videoTitleOverlay.textContent = title;
|
314 |
-
videoOutputSection.classList.remove('ion-hide');
|
315 |
-
videoOutputSection.classList.remove('opacity-0');
|
316 |
-
const statusMessagesDiv = document.getElementById('statusMessages');
|
317 |
-
statusMessagesDiv.innerHTML = `<p class="ion-text-color-secondary"><ion-icon icon="video" color="primary" style="margin-right: 4px;"></ion-icon>Loading: ${title}</p>`;
|
318 |
-
statusMessagesDiv.scrollTop = statusMessagesDiv.scrollHeight;
|
319 |
-
});
|
320 |
-
});
|
321 |
|
322 |
-
// Apply Settings Button Simulation
|
323 |
-
document.getElementById('applySettingsButton').addEventListener('click', function() {
|
324 |
-
const statusMessagesDiv = document.getElementById('statusMessages');
|
325 |
-
statusMessagesDiv.innerHTML = `<p class="ion-text-color-success"><ion-icon icon="checkmark-circle" color="success" style="margin-right: 4px;"></ion-icon>Settings Applied.</p>`;
|
326 |
-
statusMessagesDiv.scrollTop = statusMessagesDiv.scrollHeight;
|
327 |
-
});
|
328 |
-
});
|
329 |
|
|
|
|
|
330 |
|
331 |
-
|
332 |
-
|
333 |
-
const duration = document.getElementById('desiredDuration').value;
|
334 |
-
const openaiKey = document.getElementById('openaiApiKey').value;
|
335 |
-
const videoGenKey = document.getElementById('videoGenApiKey').value;
|
336 |
-
const statusMessagesDiv = document.getElementById('statusMessages');
|
337 |
-
const errorMessageDiv = document.getElementById('errorMessage');
|
338 |
-
const generateButton = document.getElementById('generateButton');
|
339 |
-
const resetButton = document.getElementById('resetButton');
|
340 |
-
const statusSection = document.getElementById('statusSection');
|
341 |
-
const videoOutputSection = document.getElementById('videoOutputSection');
|
342 |
-
const videoTitleSpan = document.getElementById('videoTitle');
|
343 |
-
const videoTitleOverlay = document.getElementById('videoTitleOverlay');
|
344 |
-
const progressBar = document.getElementById('progressBar');
|
345 |
|
346 |
-
//
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
videoOutputSection.classList.remove('opacity-0');
|
351 |
-
resetButton.classList.remove('ion-hide');
|
352 |
-
generateButton.classList.add('ion-hide'); // Hide generate, show reset
|
353 |
-
resetButton.classList.remove('ion-hide');
|
354 |
-
progressBar.value = 0;
|
355 |
-
|
356 |
-
|
357 |
-
const steps = [
|
358 |
-
{ message: "Analyzing Topic...", delay: 800 },
|
359 |
-
{ message: "Generating Script...", delay: 1500 },
|
360 |
-
{ message: "Creating Voiceover...", delay: 2200 },
|
361 |
-
{ message: "Synthesizing Scenes...", delay: 2000 },
|
362 |
-
{ message: "Selecting Visuals...", delay: 1800 },
|
363 |
-
{ message: "Assembling Video...", delay: 2500 },
|
364 |
-
{ message: "Adding Effects...", delay: 1500 },
|
365 |
-
{ message: "Rendering Video...", delay: 3000 }
|
366 |
-
];
|
367 |
-
|
368 |
-
let currentStepIndex = 0;
|
369 |
-
function processStep() {
|
370 |
-
if (currentStepIndex < steps.length) {
|
371 |
-
simulateStep(statusMessagesDiv, steps[currentStepIndex].message, steps[currentStepIndex].delay, () => {
|
372 |
-
progressBar.value = ((currentStepIndex + 1) / steps.length);
|
373 |
-
currentStepIndex++;
|
374 |
-
processStep();
|
375 |
-
}, true);
|
376 |
-
} else {
|
377 |
-
progressBar.value = 1;
|
378 |
-
statusMessagesDiv.innerHTML = '<p class="ion-text-color-success"><ion-icon icon="checkmark-circle" color="success" style="margin-right: 4px;"></ion-icon>Generation Complete!</p>';
|
379 |
-
|
380 |
-
videoTitleSpan.textContent = topic + " (" + duration + "min Preview)";
|
381 |
-
videoTitleOverlay.textContent = topic;
|
382 |
-
videoOutputSection.classList.remove('ion-hide');
|
383 |
-
setTimeout(() => videoOutputSection.classList.remove('opacity-0'), 50);
|
384 |
-
generateButton.classList.remove('ion-hide'); // Show generate, hide reset
|
385 |
-
generateButton.classList.add('ion-hide');
|
386 |
-
resetButton.classList.remove('ion-hide');
|
387 |
-
}
|
388 |
}
|
389 |
-
|
390 |
|
|
|
|
|
|
|
|
|
391 |
|
392 |
-
});
|
393 |
|
394 |
-
document.
|
395 |
-
const statusMessagesDiv = document.getElementById('statusMessages');
|
396 |
-
const errorMessageDiv = document.getElementById('errorMessage');
|
397 |
const generateButton = document.getElementById('generateButton');
|
398 |
const resetButton = document.getElementById('resetButton');
|
399 |
-
const
|
|
|
400 |
const progressBar = document.getElementById('progressBar');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
401 |
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
});
|
411 |
|
412 |
|
413 |
-
|
414 |
-
|
415 |
-
const
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
}
|
429 |
-
statusDiv.appendChild(messageElement);
|
430 |
-
statusDiv.scrollTop = statusDiv.scrollHeight;
|
431 |
-
if (callback) callback();
|
432 |
-
}, delay + Math.random() * 500);
|
433 |
-
}
|
434 |
-
</script>
|
435 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
436 |
</body>
|
437 |
</html>
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
6 |
+
<title>Autotutorial.ai</title>
|
7 |
+
<link rel="manifest" href="manifest.json"> <!-- Create this file for PWA -->
|
8 |
<meta name="apple-mobile-web-app-capable" content="yes">
|
9 |
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
10 |
|
11 |
<!-- Ionic CSS -->
|
12 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css">
|
13 |
|
14 |
+
<!-- Google Fonts (Inter) -->
|
|
|
|
|
|
|
15 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
16 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
17 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
18 |
+
<style>
|
|
|
19 |
:root {
|
20 |
--ion-font-family: 'Inter', sans-serif;
|
21 |
+
/* Define app-specific colors (based on previous designs, refined)*/
|
22 |
+
--app-primary: #4f46e5; /* Indigo-600 from Tailwind/Radix */
|
23 |
+
--app-primary-rgb: 79,70,229;
|
24 |
+
--app-primary-contrast: #ffffff;
|
25 |
+
--app-primary-contrast-rgb: 255,255,255;
|
26 |
+
--app-primary-shade: #4640cc; /* Slightly darker shade */
|
27 |
+
--app-primary-tint: #615ce6; /* Slightly lighter tint */
|
28 |
+
|
29 |
+
--ion-color-primary: var(--app-primary);
|
30 |
+
--ion-color-primary-rgb: var(--app-primary-rgb);
|
31 |
+
--ion-color-primary-contrast: var(--app-primary-contrast);
|
32 |
+
--ion-color-primary-contrast-rgb: var(--app-primary-contrast-rgb);
|
33 |
+
--ion-color-primary-shade: var(--app-primary-shade);
|
34 |
+
--ion-color-primary-tint: var(--app-primary-tint);
|
35 |
}
|
36 |
+
|
37 |
+
/* Global Style Tweaks */
|
38 |
body {
|
39 |
font-family: var(--ion-font-family);
|
40 |
+
-webkit-font-smoothing: antialiased;
|
41 |
+
-moz-osx-font-smoothing: grayscale;
|
42 |
}
|
43 |
+
|
44 |
+
.video-preview-container {
|
45 |
+
border-radius: var(--ion-border-radius);
|
46 |
overflow: hidden;
|
47 |
+
position: relative; /* For overlay */
|
48 |
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Subtle shadow */
|
49 |
}
|
50 |
+
|
51 |
+
.video-title-overlay {
|
52 |
position: absolute;
|
53 |
bottom: 16px;
|
54 |
left: 16px;
|
55 |
color: white;
|
56 |
+
font-size: 1.2em;
|
57 |
font-weight: 600;
|
58 |
+
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
}
|
60 |
+
.hidden {
|
61 |
+
display: none !important;
|
62 |
}
|
63 |
+
|
64 |
</style>
|
65 |
</head>
|
66 |
<body>
|
|
|
67 |
<ion-app>
|
68 |
+
<ion-header>
|
69 |
+
<ion-toolbar color="primary">
|
|
|
70 |
<ion-title>Autotutorial.ai</ion-title>
|
71 |
+
<ion-buttons slot="end">
|
72 |
+
<ion-button id="helpButton">
|
73 |
+
<ion-icon slot="icon-only" name="help-circle"></ion-icon>
|
74 |
+
</ion-button>
|
75 |
+
</ion-buttons>
|
76 |
</ion-toolbar>
|
77 |
</ion-header>
|
78 |
|
79 |
+
<ion-content class="ion-padding">
|
80 |
+
<!-- Main Content: Generate Tutorial -->
|
81 |
+
<div id="generate-content">
|
82 |
+
|
83 |
+
<ion-card>
|
84 |
+
<ion-card-header>
|
85 |
+
<ion-card-title>Generate Tutorial</ion-card-title>
|
86 |
+
<ion-card-subtitle>Create a new video tutorial</ion-card-subtitle>
|
87 |
+
</ion-card-header>
|
88 |
+
|
89 |
+
<ion-card-content>
|
90 |
+
<ion-list lines="full">
|
91 |
+
<ion-item>
|
92 |
+
<ion-label position="floating">Tutorial Topic</ion-label>
|
93 |
+
<ion-input id="tutorialTopic" value="CSS Grid Layout"></ion-input>
|
94 |
+
</ion-item>
|
95 |
+
<ion-item>
|
96 |
+
<ion-label>Duration</ion-label>
|
97 |
+
<ion-select id="tutorialDuration" value="10">
|
98 |
+
<ion-select-option value="5">5 Minutes</ion-select-option>
|
99 |
+
<ion-select-option value="10">10 Minutes</ion-select-option>
|
100 |
+
<ion-select-option value="15">15 Minutes</ion-select-option>
|
101 |
+
<ion-select-option value="20">20 Minutes</ion-select-option>
|
102 |
+
</ion-select>
|
103 |
+
</ion-item>
|
104 |
+
<ion-item>
|
105 |
+
<ion-label position="floating">OpenAI API Key (Optional)</ion-label>
|
106 |
+
<ion-input id="openaiApiKey" type="text" placeholder="sk-..." value="sk-DEMO_OPENAI_KEY"></ion-input>
|
107 |
+
</ion-item>
|
108 |
+
<ion-item>
|
109 |
+
<ion-label position="floating">Video Gen. API Key (Optional)</ion-label>
|
110 |
+
<ion-input id="videoGenApiKey" type="text" placeholder="vg-..." value="vg-DEMO_VIDEO_KEY"></ion-input>
|
111 |
+
</ion-item>
|
112 |
+
</ion-list>
|
113 |
+
|
114 |
+
<ion-button id="generateButton" expand="block" color="primary" style="margin-top: 20px;">
|
115 |
+
<ion-icon slot="start" name="play-circle"></ion-icon>
|
116 |
+
Generate
|
117 |
+
</ion-button>
|
118 |
+
<ion-button id="resetButton" expand="block" color="medium" style="margin-top: 10px;" class="hidden">
|
119 |
+
<ion-icon slot="start" name="refresh"></ion-icon>
|
120 |
+
Reset
|
121 |
+
</ion-button>
|
122 |
+
</ion-card-content>
|
123 |
+
</ion-card>
|
124 |
+
|
125 |
+
|
126 |
+
|
127 |
+
<ion-card id="statusCard" class="hidden">
|
128 |
+
<ion-card-header>
|
129 |
+
<ion-card-title>Status</ion-card-title>
|
130 |
+
</ion-card-header>
|
131 |
+
<ion-card-content>
|
132 |
+
<ion-list id="statusList"></ion-list>
|
133 |
+
<ion-progress-bar id="progressBar" type="indeterminate"></ion-progress-bar> <!-- Changed to indeterminate -->
|
134 |
+
<ion-text color="danger" id="errorMessage" class="hidden"></ion-text>
|
135 |
+
</ion-card-content>
|
136 |
+
</ion-card>
|
137 |
+
|
138 |
+
<ion-card id="previewCard" class="hidden">
|
139 |
+
<ion-card-header>
|
140 |
+
<ion-card-title>Tutorial Preview</ion-card-title>
|
141 |
+
</ion-card-header>
|
142 |
+
<ion-card-content>
|
143 |
+
<div class="video-preview-container">
|
144 |
+
<video id="tutorialVideo" controls width="100%" preload="metadata">
|
145 |
+
<source src="https://vjs.zencdn.net/v/oceans.mp4" type="video/mp4"> <!-- Better demo video -->
|
146 |
+
Your browser does not support the video tag.
|
147 |
+
</video>
|
148 |
+
<div id="videoTitleOverlay" class="video-title-overlay"></div>
|
149 |
</div>
|
150 |
+
<ion-button expand="block" id="downloadButton" style="margin-top: 16px;">
|
151 |
+
<ion-icon slot="start" name="download"></ion-icon>
|
152 |
+
Download (Demo)
|
153 |
+
</ion-button>
|
154 |
+
</ion-card-content>
|
155 |
+
</ion-card>
|
156 |
+
</div>
|
157 |
+
|
158 |
+
|
159 |
+
<!-- Tutorial History (Initially Hidden) -->
|
160 |
+
<div id="history-content" class="hidden">
|
161 |
+
<ion-card>
|
162 |
+
<ion-card-header>
|
163 |
+
<ion-card-title>Tutorial History</ion-card-title>
|
164 |
+
<ion-card-subtitle>View your past tutorials</ion-card-subtitle>
|
165 |
+
</ion-card-header>
|
166 |
+
<ion-card-content>
|
167 |
+
<ion-list id="historyList">
|
168 |
+
<ion-item button class="history-item" data-title="React Hooks Guide">
|
169 |
+
<ion-label>React Hooks Guide</ion-label>
|
170 |
+
<ion-note slot="end">Oct 28, 2023</ion-note> <!-- Use ion-note for date -->
|
171 |
+
</ion-item>
|
172 |
+
<ion-item button class="history-item" data-title="Vue.js Composition API">
|
173 |
+
<ion-label>Vue.js Composition API</ion-label>
|
174 |
+
<ion-note slot="end">Oct 27, 2023</ion-note>
|
175 |
+
</ion-item>
|
176 |
+
<ion-item button class="history-item" data-title="Python for Data Science">
|
177 |
+
<ion-label>Python for Data Science</ion-label>
|
178 |
+
<ion-note slot="end">Oct 26, 2023</ion-note>
|
179 |
+
</ion-item>
|
180 |
+
</ion-list>
|
181 |
+
<ion-text color="medium">
|
182 |
+
<p>This is a simulated history for demo purposes.</p>
|
183 |
+
</ion-text>
|
184 |
+
</ion-card-content>
|
185 |
+
</ion-card>
|
186 |
+
</div>
|
187 |
+
|
188 |
+
<!-- Settings (Initially Hidden) -->
|
189 |
+
<div id="settings-content" class="hidden">
|
190 |
+
<ion-card>
|
191 |
+
<ion-card-header>
|
192 |
+
<ion-card-title>Settings</ion-card-title>
|
193 |
+
<ion-card-subtitle>Configure app preferences</ion-card-subtitle>
|
194 |
+
</ion-card-header>
|
195 |
+
<ion-card-content>
|
196 |
+
<ion-list lines="full">
|
197 |
+
<ion-item>
|
198 |
+
<ion-label>Default Duration</ion-label>
|
199 |
+
<ion-select id="defaultDuration" value="10">
|
200 |
+
<ion-select-option value="5">5 Minutes</ion-select-option>
|
201 |
+
<ion-select-option value="10">10 Minutes</ion-select-option>
|
202 |
+
<ion-select-option value="15">15 Minutes</ion-select-option>
|
203 |
+
<ion-select-option value="20">20 Minutes</ion-select-option>
|
204 |
+
</ion-select>
|
205 |
+
</ion-item>
|
206 |
+
<ion-item>
|
207 |
+
<ion-label>Default Quality</ion-label>
|
208 |
+
<ion-select id="defaultQuality" value="720p">
|
209 |
+
<ion-select-option value="720p">720p</ion-select-option>
|
210 |
+
<ion-select-option value="1080p">1080p</ion-select-option>
|
211 |
+
<ion-select-option value="4k">4K (Premium)</ion-select-option>
|
212 |
+
</ion-select>
|
213 |
+
</ion-item>
|
214 |
+
<ion-item>
|
215 |
+
<ion-label>Voiceover Language</ion-label>
|
216 |
+
<ion-select id="defaultVoiceover" value="en-US">
|
217 |
+
<ion-select-option value="en-US">English (US)</ion-select-option>
|
218 |
+
<ion-select-option value="en-GB">English (UK)</ion-select-option>
|
219 |
+
<ion-select-option value="es-ES">Spanish (ES)</ion-select-option>
|
220 |
+
</ion-select>
|
221 |
+
</ion-item>
|
222 |
+
<ion-item>
|
223 |
+
<ion-label>Output Format</ion-label>
|
224 |
+
<ion-select id="defaultOutput" value="mp4">
|
225 |
+
<ion-select-option value="mp4">MP4</ion-select-option>
|
226 |
+
<ion-select-option value="mov">MOV</ion-select-option>
|
227 |
+
<ion-select-option value="webm">WebM</ion-select-option>
|
228 |
+
</ion-select>
|
229 |
+
</ion-item>
|
230 |
+
</ion-list>
|
231 |
+
<ion-button id="applySettingsButton" expand="block" style="margin-top: 20px;">
|
232 |
+
<ion-icon slot="start" name="save"></ion-icon>
|
233 |
+
Apply Settings
|
234 |
+
</ion-button>
|
235 |
+
</ion-card-content>
|
236 |
+
</ion-card>
|
237 |
+
</div>
|
238 |
+
|
239 |
+
<!-- Help Modal (Initially Hidden) -->
|
240 |
+
<ion-modal id="helpModal" trigger="helpButton">
|
241 |
+
<ion-header>
|
242 |
+
<ion-toolbar>
|
243 |
+
<ion-title>Help & Support</ion-title>
|
244 |
+
<ion-buttons slot="end">
|
245 |
+
<ion-button onclick="closeHelpModal()">
|
246 |
+
<ion-icon slot="icon-only" name="close"></ion-icon>
|
247 |
+
</ion-button>
|
248 |
+
</ion-buttons>
|
249 |
+
</ion-toolbar>
|
250 |
+
</ion-header>
|
251 |
+
<ion-content class="ion-padding">
|
252 |
+
<p>Welcome to Autotutorial.ai, the app that makes creating video tutorials a breeze!</p>
|
253 |
+
<p><strong>Getting Started:</strong></p>
|
254 |
+
<ol>
|
255 |
+
<li>Navigate to the <strong>Generate Tutorial</strong> section.</li>
|
256 |
+
<li>Enter the topic of your tutorial in the <strong>Tutorial Topic</strong> field.</li>
|
257 |
+
<li>Select your desired tutorial length using the <strong>Duration</strong> dropdown.</li>
|
258 |
+
<li><em>(Optional)</em> If you have API keys for OpenAI and a video generation service, you can enter them in the respective fields for enhanced tutorial creation. This is not required for basic functionality.</li>
|
259 |
+
<li>Click the <strong>Generate</strong> button to start the automated tutorial creation process.</li>
|
260 |
+
<li>Monitor the <strong>Status</strong> section to see the progress of your tutorial generation.</li>
|
261 |
+
<li>Once complete, preview your generated tutorial in the <strong>Tutorial Preview</strong> section.</li>
|
262 |
+
</ol>
|
263 |
+
<p><strong>Tutorial History</strong> allows you to view a list of your previously generated tutorials (simulated in this demo).</p>
|
264 |
+
<p><strong>Settings</strong> let you configure default preferences for new tutorials (also simulated in this demo).</p>
|
265 |
+
<p>For any questions or support, please contact us at: <a href="mailto:[email protected]">[email protected]</a></p>
|
266 |
+
</ion-content>
|
267 |
+
</ion-modal>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
|
269 |
</ion-content>
|
270 |
|
271 |
+
<ion-footer>
|
272 |
+
<ion-toolbar>
|
273 |
+
<ion-tabs>
|
274 |
+
<ion-tab-bar slot="bottom">
|
275 |
+
<ion-tab-button tab="generate" onclick="showSection('generate')">
|
276 |
+
<ion-icon name="play-circle"></ion-icon>
|
277 |
+
<ion-label>Generate</ion-label>
|
278 |
+
</ion-tab-button>
|
279 |
+
<ion-tab-button tab="history" onclick="showSection('history')">
|
280 |
+
<ion-icon name="time"></ion-icon>
|
281 |
+
<ion-label>History</ion-label>
|
282 |
+
</ion-tab-button>
|
283 |
+
<ion-tab-button tab="settings" onclick="showSection('settings')">
|
284 |
+
<ion-icon name="settings"></ion-icon>
|
285 |
+
<ion-label>Settings</ion-label>
|
286 |
+
</ion-tab-button>
|
287 |
+
</ion-tab-bar>
|
288 |
+
</ion-tabs>
|
289 |
+
</ion-toolbar>
|
290 |
+
</ion-footer>
|
291 |
|
292 |
</ion-app>
|
293 |
|
294 |
+
<!-- Ionic JavaScript -->
|
295 |
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@7/dist/ionic/ionic.esm.js"></script>
|
296 |
+
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@7/dist/ionic/ionic.js"></script>
|
297 |
|
298 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
299 |
|
300 |
+
function showSection(sectionId) {
|
301 |
+
// Hide all content sections
|
302 |
+
document.getElementById('generate-content').classList.add('hidden');
|
303 |
+
document.getElementById('history-content').classList.add('hidden');
|
304 |
+
document.getElementById('settings-content').classList.add('hidden');
|
305 |
|
306 |
+
// Remove active class from all tab buttons. Important for visual state.
|
307 |
+
document.querySelectorAll('ion-tab-button').forEach(btn => btn.classList.remove('tab-selected'));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
|
310 |
+
// Show the selected section
|
311 |
+
document.getElementById(`${sectionId}-content`).classList.remove('hidden');
|
312 |
|
313 |
+
// Highlight active tab button
|
314 |
+
document.querySelector(`ion-tab-button[tab="${sectionId}"]`).classList.add('tab-selected');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
315 |
|
316 |
+
// Hide preview and status cards when switching tabs *away* from generate
|
317 |
+
if (sectionId !== 'generate') {
|
318 |
+
document.getElementById('previewCard').classList.add('hidden');
|
319 |
+
document.getElementById('statusCard').classList.add('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
}
|
321 |
+
}
|
322 |
|
323 |
+
function closeHelpModal() {
|
324 |
+
const modal = document.getElementById('helpModal');
|
325 |
+
modal.dismiss();
|
326 |
+
}
|
327 |
|
|
|
328 |
|
329 |
+
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
330 |
const generateButton = document.getElementById('generateButton');
|
331 |
const resetButton = document.getElementById('resetButton');
|
332 |
+
const statusCard = document.getElementById('statusCard');
|
333 |
+
const statusList = document.getElementById('statusList');
|
334 |
const progressBar = document.getElementById('progressBar');
|
335 |
+
const previewCard = document.getElementById('previewCard');
|
336 |
+
const videoTitleOverlay = document.getElementById('videoTitleOverlay');
|
337 |
+
const tutorialVideo = document.getElementById('tutorialVideo');
|
338 |
+
const errorMessage = document.getElementById('errorMessage');
|
339 |
+
|
340 |
+
// History click handler (simulated "load")
|
341 |
+
const historyList = document.getElementById('historyList');
|
342 |
+
historyList.addEventListener('click', (event) => {
|
343 |
+
const item = event.target.closest('.history-item'); // Find closest item
|
344 |
+
if (item) {
|
345 |
+
const title = item.dataset.title;
|
346 |
+
videoTitleOverlay.textContent = title;
|
347 |
+
previewCard.classList.remove('hidden'); // Show preview card
|
348 |
+
// You could simulate loading different videos here if you have multiple sources
|
349 |
+
// tutorialVideo.src = 'another_video.mp4';
|
350 |
+
// tutorialVideo.load();
|
351 |
+
statusCard.classList.remove('hidden'); // Show status
|
352 |
+
statusList.innerHTML = `<ion-item><ion-label color="primary">Loading ${title}...</ion-label></ion-item>`; // Show loading message
|
353 |
+
|
354 |
+
}
|
355 |
|
356 |
+
});
|
357 |
+
|
358 |
+
// Apply Settings simulation
|
359 |
+
document.getElementById('applySettingsButton').addEventListener('click', () => {
|
360 |
+
// Simulate setting changes (could visually update UI elements if desired)
|
361 |
+
statusCard.classList.remove('hidden');
|
362 |
+
statusList.innerHTML = `<ion-item><ion-label color="success">Settings applied!</ion-label></ion-item>`;
|
363 |
+
});
|
|
|
364 |
|
365 |
|
366 |
+
// Generate Button Click Handler
|
367 |
+
generateButton.addEventListener('click', () => {
|
368 |
+
const topic = document.getElementById('tutorialTopic').value;
|
369 |
+
const duration = document.getElementById('tutorialDuration').value;
|
370 |
+
const openaiKey = document.getElementById('openaiApiKey').value;
|
371 |
+
const videoGenKey = document.getElementById('videoGenApiKey').value;
|
372 |
+
|
373 |
+
// Clear previous status and error messages
|
374 |
+
statusList.innerHTML = '';
|
375 |
+
errorMessage.textContent = '';
|
376 |
+
errorMessage.classList.add('hidden');
|
377 |
+
|
378 |
+
// Show status and progress bar
|
379 |
+
statusCard.classList.remove('hidden');
|
380 |
+
progressBar.type = 'indeterminate'; // Start indeterminate
|
381 |
+
|
382 |
+
// Hide preview card (until generation is complete)
|
383 |
+
previewCard.classList.add('hidden');
|
384 |
+
|
385 |
+
// Hide Generate, show Reset
|
386 |
+
generateButton.classList.add('hidden');
|
387 |
+
resetButton.classList.remove('hidden');
|
388 |
+
|
389 |
+
const steps = [
|
390 |
+
{ message: "Analyzing Topic...", delay: 800 },
|
391 |
+
{ message: "Generating Script Outline (AI)...", delay: 1500 },
|
392 |
+
{ message: "Writing Script Content (AI)...", delay: 2200 },
|
393 |
+
{ message: "Synthesizing Voiceover (AI)...", delay: 1800 },
|
394 |
+
{ message: "Selecting Relevant Visuals...", delay: 1600 },
|
395 |
+
{ message: "Assembling Video Scenes...", delay: 2400 },
|
396 |
+
{ message: "Adding Transitions and Effects...", delay: 1500 },
|
397 |
+
{ message: "Rendering Final Video...", delay: 3000 }
|
398 |
+
];
|
399 |
+
|
400 |
+
let currentStep = 0;
|
401 |
+
|
402 |
+
function simulateStep() {
|
403 |
+
if (currentStep < steps.length) {
|
404 |
+
const listItem = document.createElement('ion-item');
|
405 |
+
listItem.innerHTML = `<ion-label>${steps[currentStep].message}</ion-label>`;
|
406 |
+
statusList.appendChild(listItem);
|
407 |
+
|
408 |
+
// Simulate varying delays
|
409 |
+
const delay = steps[currentStep].delay + (Math.random() * 500 - 250); // +/- 250ms variation
|
410 |
+
|
411 |
+
setTimeout(() => {
|
412 |
+
currentStep++;
|
413 |
+
simulateStep();
|
414 |
+
}, delay);
|
415 |
+
|
416 |
+
} else {
|
417 |
+
// Generation "complete"
|
418 |
+
progressBar.type = 'determinate'; // Switch to determinate for final "fill"
|
419 |
+
progressBar.value = 1; // Set to 100%
|
420 |
+
|
421 |
+
statusList.innerHTML = `<ion-item><ion-label color="success">Tutorial generation complete!</ion-label></ion-item>`;
|
422 |
+
|
423 |
+
videoTitleOverlay.textContent = `${topic} (${duration} min)`;
|
424 |
+
previewCard.classList.remove('hidden');
|
425 |
+
|
426 |
+
// Show generate, hide reset
|
427 |
+
generateButton.classList.remove('hidden');
|
428 |
+
resetButton.classList.add('hidden');
|
429 |
+
}
|
430 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
431 |
|
432 |
+
simulateStep();
|
433 |
+
});
|
434 |
+
|
435 |
+
// Reset Button Click Handler
|
436 |
+
resetButton.addEventListener('click', () => {
|
437 |
+
// Clear form fields
|
438 |
+
document.getElementById('tutorialTopic').value = '';
|
439 |
+
document.getElementById('tutorialDuration').value = '10';
|
440 |
+
document.getElementById('openaiApiKey').value = '';
|
441 |
+
document.getElementById('videoGenApiKey').value = '';
|
442 |
+
|
443 |
+
// Hide status, error, and preview cards
|
444 |
+
statusCard.classList.add('hidden');
|
445 |
+
errorMessage.classList.add('hidden');
|
446 |
+
previewCard.classList.add('hidden');
|
447 |
+
|
448 |
+
// Clear status list
|
449 |
+
statusList.innerHTML = '';
|
450 |
+
|
451 |
+
// Reset progress bar
|
452 |
+
progressBar.value = 0;
|
453 |
+
progressBar.type = 'indeterminate';
|
454 |
+
|
455 |
+
// Show Generate, hide Reset
|
456 |
+
generateButton.classList.remove('hidden');
|
457 |
+
resetButton.classList.add('hidden');
|
458 |
+
});
|
459 |
+
|
460 |
+
document.getElementById('downloadButton').addEventListener('click', () => {
|
461 |
+
// Simulate download
|
462 |
+
statusCard.classList.remove('hidden');
|
463 |
+
statusList.innerHTML = `<ion-item><ion-label color="primary">Downloading (simulated)...</ion-label></ion-item>`;
|
464 |
+
});
|
465 |
+
});
|
466 |
+
</script>
|
467 |
</body>
|
468 |
</html>
|