canti primo tag

This commit is contained in:
David Frassi
2026-05-16 17:24:59 +02:00
commit 0c33fc6fcf
106 changed files with 28210 additions and 0 deletions
+211
View File
@@ -0,0 +1,211 @@
<ion-header [translucent]="true" class="ion-no-border">
<ion-toolbar class="bg-gradient top-toolbar">
<ion-buttons slot="start">
<div class="offline-badge-header" *ngIf="!connectivityService.isOnline()">
<ion-icon name="cloud-offline-outline"></ion-icon>
</div>
</ion-buttons>
<ion-title class="outfit-font wrapped-title">
<div class="title-main" [style.fontSize.rem]="fontSize() * 1.1">
<span class="canto-number" *ngIf="canto()?.id_canti">{{ canto()?.id_canti }}</span>
<span class="title-text">{{ canto()?.titolo || 'Player' }}</span>
</div>
</ion-title>
<ion-buttons slot="start">
<ion-back-button text="" defaultHref="/home" color="secondary"></ion-back-button>
</ion-buttons>
</ion-toolbar>
<!-- Audio Toolbar Removed -->
</ion-header>
<ion-content class="bg-gradient" [class.full-screen-content]="settingsService.fullscreenMode()">
<div class="lyrics-container"
[style.fontSize.rem]="fontSize()"
[class.full-screen-container]="settingsService.fullscreenMode()"
(touchstart)="onTouchStart($event)"
(touchmove)="onTouchMove($event)"
(touchend)="onTouchEnd()">
<!-- Landscape Side Controls (Scrollable) -->
<div class="landscape-side-controls" [class.active-fullscreen]="settingsService.fullscreenMode()">
<div class="side-scroll-container">
<div class="side-group equidistant-group">
<!-- Navigation -->
<ion-button fill="clear" (click)="restart()">
<ion-icon name="arrow-up-circle" color="secondary"></ion-icon>
</ion-button>
<ion-button fill="clear" (click)="prev()" [disabled]="currentLineIndex() === 0">
<ion-icon name="chevron-up" color="secondary"></ion-icon>
</ion-button>
<ion-button fill="clear" (click)="next()" [disabled]="currentLineIndex() === getTotalLines() - 1">
<ion-icon name="chevron-down" color="secondary"></ion-icon>
</ion-button>
<!-- Zoom -->
<ion-button fill="clear" (click)="zoomIn()" [disabled]="fontSize() >= 5.0">
<ion-icon name="add-circle-outline" color="secondary"></ion-icon>
</ion-button>
<ion-button fill="clear" (click)="zoomOut()" [disabled]="fontSize() <= 0.6">
<ion-icon name="remove-circle-outline" color="secondary"></ion-icon>
</ion-button>
<!-- Karaoke Toggle (Moved down) -->
<ion-button fill="clear" (click)="toggleListening()" [color]="audioEngine.isListening() ? 'danger' : 'secondary'">
<ion-icon [name]="audioEngine.isListening() ? 'mic' : 'mic-off'"></ion-icon>
</ion-button>
<!-- Youtube Link -->
<ion-button *ngIf="canto()?.link_youtube && canto()?.link_youtube!.length > 5" fill="clear" (click)="openYoutube()">
<ion-icon name="logo-youtube" color="danger"></ion-icon>
</ion-button>
</div>
</div>
</div>
<!-- Sections rendering -->
<div class="lyrics-view">
<div *ngFor="let section of parsedSections(); let si = index"
class="section"
[class.chorus]="section.type === 'chorus'"
[class.verse-num]="section.type === 'verse_num'">
<!-- Section label for chorus -->
<div *ngIf="section.type === 'chorus'" class="section-label">Rit.</div>
<div *ngIf="section.type === 'verse_num' && section.verseNumber" class="section-label verse-num-label">{{ section.verseNumber }}.</div>
<div *ngFor="let line of section.lines; let li = index"
class="lyric-line"
[class.active]="isActiveLine(si, li)">
<!-- Chord mode: show chords above text -->
<ng-container *ngIf="showChords(); else textOnly">
<span *ngFor="let seg of line.segments" class="chord-segment">
<span *ngIf="seg.chord" class="chord">{{ seg.chord }}</span>
<span class="seg-text">{{ seg.text }}</span>
</span>
</ng-container>
<!-- Text only mode -->
<ng-template #textOnly>
{{ line.text }}
</ng-template>
</div>
</div>
</div>
</div>
</ion-content>
<ion-footer class="ion-no-border">
<!-- Audio Player Toolbar -->
<ion-toolbar class="global-player-toolbar glass" *ngIf="canto()?.link_youtube && canto()?.link_youtube!.length > 5 && !settingsService.fullscreenMode()">
<div class="player-content">
<div class="controls-row">
<ion-button fill="clear" color="secondary" (click)="prevSong()" class="skip-btn">
<ion-icon slot="icon-only" name="play-skip-back-sharp"></ion-icon>
</ion-button>
<ion-button fill="clear" color="secondary" (click)="toggleAudio()" class="play-btn">
<ion-icon slot="icon-only" [name]="youtubePlayerService.isPlaying() ? 'pause-sharp' : 'play-sharp'"></ion-icon>
</ion-button>
<ion-range
[min]="0"
[max]="youtubePlayerService.videoDuration()"
[value]="youtubePlayerService.videoProgress()"
(ionChange)="onSeek($event)"
color="secondary"
class="global-range">
</ion-range>
<ion-button fill="clear" color="secondary" (click)="nextSong()" class="skip-btn">
<ion-icon slot="icon-only" name="play-skip-forward-sharp"></ion-icon>
</ion-button>
<ion-button fill="clear" color="medium" (click)="stopVideo($event)" class="close-btn">
<ion-icon slot="icon-only" name="close-circle-outline"></ion-icon>
</ion-button>
</div>
</div>
</ion-toolbar>
<!-- Voice Activity Visualizer (Slim overlay) -->
<div class="transcript-area slim" *ngIf="audioEngine.isListening()">
<div class="energy-bar" [style.width.%]="math.min(100, audioEngine.energyLevel() * 3)"></div>
</div>
<ion-toolbar class="bg-gradient slim-toolbar">
<div class="slim-controls">
<!-- Karaoke Toggle in Portrait -->
<ion-button fill="clear" size="small" (click)="toggleListening()" [color]="audioEngine.isListening() ? 'danger' : 'secondary'" class="mic-btn-portrait">
<ion-icon slot="icon-only" [name]="audioEngine.isListening() ? 'mic' : 'mic-off'"></ion-icon>
</ion-button>
<!-- Transposition (Only in chords mode) -->
<div class="group" *ngIf="showChords()">
<ion-button fill="clear" size="small" (click)="transposeDown()">
<ion-icon slot="icon-only" name="remove"></ion-icon>
</ion-button>
<div class="transpose-indicator">
<ion-icon name="musical-note" color="secondary"></ion-icon>
<span class="val" *ngIf="transposeAmount() !== 0">{{ transposeAmount() > 0 ? '+' : '' }}{{ transposeAmount() }}</span>
</div>
<ion-button fill="clear" size="small" (click)="transposeUp()">
<ion-icon slot="icon-only" name="add"></ion-icon>
</ion-button>
</div>
<!-- Navigation -->
<div class="group">
<ion-button fill="clear" size="small" (click)="restart()">
<ion-icon slot="icon-only" name="arrow-up-circle" color="secondary"></ion-icon>
</ion-button>
<ion-button fill="clear" size="small" (click)="prev()" [disabled]="currentLineIndex() === 0">
<ion-icon slot="icon-only" name="chevron-back" color="secondary"></ion-icon>
</ion-button>
<ion-button fill="clear" size="small" (click)="next()" [disabled]="currentLineIndex() === getTotalLines() - 1">
<ion-icon slot="icon-only" name="chevron-forward" color="secondary"></ion-icon>
</ion-button>
</div>
<!-- Zoom -->
<div class="group">
<ion-button fill="clear" size="small" (click)="zoomOut()" [disabled]="fontSize() <= 0.6">
<ion-icon slot="icon-only" name="remove-circle-outline" color="secondary"></ion-icon>
</ion-button>
<ion-button fill="clear" size="small" (click)="zoomIn()" [disabled]="fontSize() >= 5.0">
<ion-icon slot="icon-only" name="add-circle-outline" color="secondary"></ion-icon>
</ion-button>
</div>
<!-- Actions -->
<div class="group">
<ion-button *ngIf="canto()?.link_youtube && canto()?.link_youtube!.length > 5" fill="clear" size="small" (click)="openYoutube()">
<ion-icon slot="icon-only" name="logo-youtube" color="danger"></ion-icon>
</ion-button>
</div>
</div>
</ion-toolbar>
</ion-footer>
<!-- Vertical Sensitivity Slider Overlay -->
<div class="mic-sensitivity-overlay" *ngIf="showSensitivitySlider() && audioEngine.isListening()">
<div class="slider-card glass">
<ion-button fill="clear" color="secondary" (click)="toggleSensitivitySlider($event)" class="close-slider-btn">
<ion-icon name="close-outline"></ion-icon>
</ion-button>
<div class="slider-wrapper"
(touchstart)="handleSensitivityTouch($event)"
(touchmove)="handleSensitivityTouch($event)">
<div class="custom-vertical-slider">
<div class="slider-track"></div>
<div class="slider-fill" [style.height.%]="(audioEngine.sensitivity() - 50) * 2"></div>
<div class="slider-knob" [style.bottom.%]="(audioEngine.sensitivity() - 50) * 2"></div>
</div>
</div>
<span class="sensitivity-label outfit-font">{{ audioEngine.sensitivity() }}%</span>
</div>
</div>