py-learn / src /app /pronunciation /pronunciation.component.html
Oviya
update pron component
0b92c10
<div class="pron-container">
<!-- Header -->
<div class="header">
<div class="dropdown-row" aria-hidden="false">
<label for="pronModeSelect" class="visually-hidden">Pronunciation Mode</label>
<select id="pronModeSelect" class="select-dropdown" aria-label="Pronunciation Mode" (change)="onModeChange($any($event.target).value)">
<option value="phonetics">Phoneme-Level Accuracy Check</option>
<option value="waveform">Audio Similarity Check</option>
<option value="normaltext" disabled>Speech-to-Text Accuracy Check</option>
<option value="prosody" disabled>Prosody Check</option>
<option value="embedding" disabled>Embedding Similarity Check</option>
</select>
</div>
<h2 class="title">Pronunciation Trainer</h2>
</div>
<!-- Main Content -->
<div class="main-content">
<!-- Left Side - Apple Image -->
<div class="image-section">
<img [src]="imgsrc" [alt]="word" class="apple-image">
</div>
<!-- Right Side - Word and Controls -->
<div class="controls-section">
<!-- Word with Phonetics -->
<div class="word-section">
<h2 class="word">{{ word }}</h2>
<p class="phonetics">{{ phonetics }}</p>
</div>
<!-- Teacher Audio Button -->
<div class="teacher-section">
<img src="assets/images/chat/natasha.png" alt="Teacher" class="avatar">
<button class="btn btn-primary"
id="playTeacherBtn"
(click)="playTeacherAudio()"
[disabled]="isTeacherLoading"
[attr.aria-busy]="isTeacherLoading">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="5 3 19 12 5 21 5 3"></polygon>
</svg>
<span class="btn-text" *ngIf="!isTeacherLoading">Play Teacher</span>
<span class="btn-text" *ngIf="isTeacherLoading">Loading...</span>
<svg class="spinner" viewBox="0 0 24 24" [class.hidden]="!isTeacherLoading">
<circle class="spinner-circle" cx="12" cy="12" r="10"></circle>
</svg>
</button>
<div class="voice-selection-container">
<div class="voice-toggle-control">
<div class="voice-label-group toggle-label-left"
[class.active]="!isOriginal"
(click)="isOriginal = false; updateSelection()">
<span class="voice-text">Original Voice</span>
</div>
<div class="voice-label-group toggle-label-right"
[class.active]="isOriginal"
(click)="isOriginal = true; updateSelection()">
<span class="voice-text">Cloned Voice</span>
</div>
</div>
</div>
</div>
<!-- Student Recording Section -->
<div class="student-section">
<img src="assets/images/pron/student.png" alt="Student" class="avatar">
<div class="button-group">
<button class="btn btn-secondary record-btn"
id="recordBtn"
(click)="isRecording ? stopRecording() : startRecording()"
[class.recording]="isRecording"
[attr.aria-pressed]="isRecording">
<ng-container *ngIf="!isRecording; else stopTpl">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
<line x1="12" x2="12" y1="19" y2="22"></line>
</svg>
<span class="btn-text">Start Recording</span>
</ng-container>
<ng-template #stopTpl>
<svg class="icon stop-icon" viewBox="0 0 24 24" fill="currentColor" stroke="none" width="18" height="18" aria-hidden="true">
<rect x="6" y="6" width="12" height="12" rx="2"></rect>
</svg>
<span class="btn-text">Stop</span>
</ng-template>
</button>
<!-- Play Student Recording (shows when a recording exists) -->
<button class="btn btn-accent"
id="playStudentBtn"
*ngIf="recordedBlobUrl"
(click)="playRecorded()">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="5 3 19 12 5 21 5 3"></polygon>
</svg>
<span class="btn-text">Play My Recording</span>
</button>
</div>
</div>
<!-- Waveform containers (shown only in Audio Similarity Check mode) -->
<div *ngIf="pronMode === 'waveform'" class="waveform-row">
<div class="waveform-block">
<div class="waveform-title">Teacher</div>
<div id="teacherWaveform" class="waveform-canvas"></div>
</div>
<div class="waveform-block">
<div class="waveform-title">Student</div>
<div id="studentWaveform" class="waveform-canvas"></div>
</div>
</div>
<!-- Check Button -->
<button class="btn btn-success full-width"
id="checkBtn"
(click)="checkPronunciation()"
[disabled]="isChecking || !recordedBlobUrl"
[attr.aria-busy]="isChecking"
[attr.aria-disabled]="isChecking || !recordedBlobUrl"
aria-label="Check pronunciation">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" *ngIf="!isChecking">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
<span class="btn-text" *ngIf="!isChecking">Check Pronunciation</span>
<span class="btn-text" *ngIf="isChecking">Checking...</span>
<svg class="spinner" viewBox="0 0 24 24" *ngIf="isChecking">
<circle class="spinner-circle" cx="12" cy="12" r="10"></circle>
</svg>
</button>
</div>
<div class="results-section" id="resultsSection">
<!-- Score Display: always shown (initially 0) so speakometer is visible from start -->
<div class="gauge-wrapper">
<div class="gauge">
<div class="gauge-arc"></div>
<!-- set CSS variable from Angular -->
<div class="needle" [style.--angle]="needleAngle + 'deg'" aria-hidden="true"></div>
</div>
<div class="mic-badge">
<span class="score-span" aria-live="polite">{{ result?.score ?? 0 }}%</span>
</div>
</div>
<!-- Suggestions: show whenever we have suggestions, regardless of result -->
<div class="suggestions-section" *ngIf="suggestions.length > 0">
<h3 class="suggestions-title">Feedback & Suggestions</h3>
<!-- Structured suggestions -->
<div class="sugg-div">
<div class="suggestions-page">
<div class="suggestion-card" *ngFor="let s of pagedSuggestions; let i = index">
<div class="suggestion-badge" aria-hidden="true">{{ s.id ?? (suggestionPage * suggestionsPerPage + i + 1) }}</div>
<div class="suggestion-body">
<div class="suggestion-point">{{ s.title }}</div>
<div class="suggestion-feedback" [innerHTML]="s.message"></div>
</div>
</div>
</div>
<!-- Pagination controls -->
<div class="suggestion-nav">
<button class="btn btn-outline btn-nav pagebtn" (click)="prevSuggestion()" [disabled]="suggestionPage === 0" aria-label="Previous feedback"></button>
<div class="nav-info">
{{ suggestionPage + 1 }} / {{ totalSuggestionPages }}
</div>
<button class="btn btn-outline btn-nav pagebtn" (click)="nextSuggestion()" [disabled]="suggestionPage >= totalSuggestionPages - 1" aria-label="Next feedback"></button>
</div>
</div>
</div>
<!-- Legacy single-string suggestion: hide in select mode and only show when not using structured suggestions -->
<div class="suggestions-section" *ngIf="pronMode !== 'select' && (!suggestions || suggestions.length === 0) && result?.suggestion">
<h3 class="suggestions-title">Feedback & Suggestions</h3>
<div class="suggestion-item">
<div class="suggestion-content">
<p class="suggestion-feedback">{{ result?.suggestion }}</p>
</div>
</div>
</div>
<div>
<div class="nav-buttons">
<button class="btn btn-outline btn-nav" (click)="prevQuestion()" [disabled]="currentIndex === 0" aria-label="Previous">
◀ Prev
</button>
<div class="nav-info">
{{ currentIndex + 1 }} / {{ questions.length }}
</div>
<button class="btn btn-outline btn-nav" (click)="nextQuestion()" [disabled]="currentIndex === questions.length - 1" aria-label="Next">
Next ▶
</button>
</div>
</div>
</div>
<button aria-label="Close" class="user-guide-close-icon" (click)="closePopup()">×</button>
<!-- Add a hidden audio element for programmatic playback -->
</div>
</div>