muboboev's picture
Подэтап 5.2 — VideoRole
9909792 verified
class AIDialog extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.partnerVideo = null;
this.userVideo = null;
this.mediaStream = null;
this.peerConnection = null;
this.aiMessages = [];
this.userMessages = [];
this.dialogAnalysis = null;
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
margin: 2rem 0;
}
.container {
background: rgba(15, 23, 42, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 1rem;
padding: 2rem;
}
.header {
display: flex;
align-items: center;
margin-bottom: 1.5rem;
}
.icon {
width: 3rem;
height: 3rem;
background: rgba(124, 58, 237, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 1rem;
}
h2 {
font-size: 1.5rem;
font-weight: 600;
margin: 0;
background: linear-gradient(90deg, #7c3aed 0%, #2563eb 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.video-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-bottom: 1.5rem;
}
.video-box {
position: relative;
border-radius: 0.5rem;
overflow: hidden;
background: #1e293b;
aspect-ratio: 16/9;
}
video {
width: 100%;
height: 100%;
object-fit: cover;
}
.video-label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.7);
padding: 0.5rem;
font-size: 0.875rem;
text-align: center;
}
.controls {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
button {
flex: 1;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
cursor: pointer;
transition: all 0.2s;
border: none;
}
.start-btn {
background: #7c3aed;
color: white;
}
.start-btn:hover {
background: #6d28d9;
}
.end-btn {
background: #1e293b;
color: white;
border: 1px solid #334155;
}
.end-btn:hover {
background: #334155;
}
.analysis {
margin-top: 1.5rem;
display: none;
}
.metric {
display: flex;
justify-content: space-between;
margin-bottom: 0.75rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.metric-label {
font-weight: 500;
}
.metric-value {
font-weight: 600;
color: #7c3aed;
}
.progress-bar {
height: 8px;
border-radius: 4px;
background: rgba(124, 58, 237, 0.2);
margin-top: 0.5rem;
}
.progress-fill {
height: 100%;
border-radius: 4px;
background: linear-gradient(90deg, #7c3aed 0%, #2563eb 100%);
width: 0%;
transition: width 0.3s ease;
}
.feedback {
margin-top: 1rem;
padding: 1rem;
background: rgba(30, 41, 59, 0.5);
border-radius: 0.5rem;
}
</style>
<div class="container">
<div class="header">
<div class="icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="9" cy="7" r="4"></circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</svg>
</div>
<h2>AI Dialog Partner</h2>
</div>
<p>Practice conversational skills with our AI partner. The system will analyze your performance.</p>
<div class="video-container">
<div class="video-box">
<video id="partnerVideo" autoplay muted></video>
<div class="video-label">AI Partner</div>
</div>
<div class="video-box">
<video id="userVideo" autoplay></video>
<div class="video-label">You</div>
</div>
</div>
<div class="controls">
<button class="start-btn" id="startBtn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<circle cx="12" cy="12" r="3"></circle>
</svg>
Start Conversation
</button>
<button class="end-btn" id="endBtn" disabled>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="6" y="4" width="4" height="16"></rect>
<rect x="14" y="4" width="4" height="16"></rect>
</svg>
End Session
</button>
</div>
<div class="analysis" id="analysis">
<h3>Dialog Analysis</h3>
<div class="metric">
<span class="metric-label">Fluency</span>
<span class="metric-value" id="fluencyScore">0%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="fluencyBar"></div>
</div>
<div class="metric">
<span class="metric-label">Vocabulary</span>
<span class="metric-value" id="vocabularyScore">0%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="vocabularyBar"></div>
</div>
<div class="metric">
<span class="metric-label">Pronunciation</span>
<span class="metric-value" id="pronunciationScore">0%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="pronunciationBar"></div>
</div>
<div class="feedback">
<h4>Feedback:</h4>
<p id="feedbackText">Complete a conversation to receive feedback.</p>
</div>
</div>
</div>
`;
this.partnerVideo = this.shadowRoot.getElementById('partnerVideo');
this.userVideo = this.shadowRoot.getElementById('userVideo');
this.startBtn = this.shadowRoot.getElementById('startBtn');
this.endBtn = this.shadowRoot.getElementById('endBtn');
this.analysisSection = this.shadowRoot.getElementById('analysis');
this.setupEventListeners();
}
setupEventListeners() {
this.startBtn.addEventListener('click', () => this.startConversation());
this.endBtn.addEventListener('click', () => this.endConversation());
}
async startConversation() {
try {
// Start user media
this.mediaStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
this.userVideo.srcObject = this.mediaStream;
// Initialize WebRTC connection (simplified for demo)
this.peerConnection = new RTCPeerConnection();
this.mediaStream.getTracks().forEach(track => {
this.peerConnection.addTrack(track, this.mediaStream);
});
// Simulate AI partner video (in real app this would be WebRTC stream)
this.partnerVideo.src = 'https://static.photos/people/640x360/1';
this.startBtn.disabled = true;
this.endBtn.disabled = false;
// Start conversation with AI
this.simulateAIDialog();
} catch (error) {
console.error('Error starting conversation:', error);
alert('Could not access camera/microphone. Please check permissions.');
}
}
async endConversation() {
// Stop media tracks
if (this.mediaStream) {
this.mediaStream.getTracks().forEach(track => track.stop());
}
// Close WebRTC connection
if (this.peerConnection) {
this.peerConnection.close();
}
this.userVideo.srcObject = null;
this.partnerVideo.src = null;
this.startBtn.disabled = false;
this.endBtn.disabled = true;
// Analyze dialog
this.analyzeDialog();
}
simulateAIDialog() {
// Simulate AI responses (in real app this would use GPT API)
const aiResponses = [
"Hello! How are you today?",
"That's interesting. Tell me more about that.",
"I understand how you feel. What would you like to discuss next?",
"Your pronunciation is improving! Keep practicing.",
"Let's try to use some new vocabulary words."
];
let responseIndex = 0;
this.aiMessages = [];
const aiInterval = setInterval(() => {
if (!this.endBtn.disabled) {
const response = aiResponses[responseIndex % aiResponses.length];
this.aiMessages.push(response);
responseIndex++;
// Update UI with AI response
const feedback = this.shadowRoot.getElementById('feedbackText');
feedback.textContent = `AI: ${response}`;
} else {
clearInterval(aiInterval);
}
}, 8000);
}
analyzeDialog() {
// Simulate dialog analysis (in real app this would use speech-to-text and NLP)
this.dialogAnalysis = {
fluency: Math.floor(Math.random() * 30) + 70,
vocabulary: Math.floor(Math.random() * 30) + 70,
pronunciation: Math.floor(Math.random() * 30) + 70,
feedback: "Good job! You showed improvement in fluency. Try to use more complex sentences next time."
};
// Display analysis
this.analysisSection.style.display = 'block';
this.shadowRoot.getElementById('fluencyScore').textContent = `${this.dialogAnalysis.fluency}%`;
this.shadowRoot.getElementById('fluencyBar').style.width = `${this.dialogAnalysis.fluency}%`;
this.shadowRoot.getElementById('vocabularyScore').textContent = `${this.dialogAnalysis.vocabulary}%`;
this.shadowRoot.getElementById('vocabularyBar').style.width = `${this.dialogAnalysis.vocabulary}%`;
this.shadowRoot.getElementById('pronunciationScore').textContent = `${this.dialogAnalysis.pronunciation}%`;
this.shadowRoot.getElementById('pronunciationBar').style.width = `${this.dialogAnalysis.pronunciation}%`;
this.shadowRoot.getElementById('feedbackText').textContent = this.dialogAnalysis.feedback;
}
}
customElements.define('ai-dialog', AIDialog);