Premiere version

This commit is contained in:
2026-03-26 22:47:00 +01:00
commit aa7bba900b

191
public/index.html Normal file
View File

@@ -0,0 +1,191 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Soundboard Pro - Expert Edition</title>
<style>
:root { --bg: #0f0f0f; --panel-bg: #1e1e1e; --accent: #ffb703; --text: #eee; }
body { background: var(--bg); color: var(--text); margin: 0; font-family: sans-serif; overflow: hidden; }
header { height: 50px; background: #222; display: flex; align-items: center; padding: 0 15px; justify-content: space-between; border-bottom: 2px solid #333; }
.grid { display: grid; grid-template-columns: repeat(5, 1fr); grid-template-rows: repeat(6, 1fr); gap: 5px; height: calc(100vh - 50px); padding: 5px; box-sizing: border-box; }
.btn { background: #2a2a2a; border: 1px solid #444; border-radius: 8px; display: flex; align-items: center; justify-content: center; position: relative; font-size: 0.7rem; text-align: center; }
.btn.active { border-color: var(--accent); color: var(--accent); }
.btn.playing { background: #e63946; color: white; box-shadow: 0 0 10px #e63946; }
.btn.has-loop::after { content: "∞"; position: absolute; top: 2px; right: 5px; font-size: 12px; color: var(--accent); }
/* Overlay / Panneau d'édition */
#editOverlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.8); display: none; z-index: 100;
align-items: flex-end;
}
#editPanel {
background: var(--panel-bg); width: 100%; padding: 20px;
border-radius: 20px 20px 0 0; box-sizing: border-box;
transform: translateY(100%); transition: transform 0.3s ease;
}
#editOverlay.show { display: flex; }
#editOverlay.show #editPanel { transform: translateY(0); }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; color: #888; font-size: 0.9rem; }
input[type="text"], input[type="range"] { width: 100%; padding: 10px; background: #333; border: 1px solid #444; color: white; border-radius: 5px; box-sizing: border-box; }
.actions { display: flex; gap: 10px; margin-top: 20px; }
.actions button { flex: 1; padding: 15px; border: none; border-radius: 8px; font-weight: bold; cursor: pointer; }
.btn-save { background: var(--accent); color: black; }
.btn-cancel { background: #444; color: white; }
.btn-file { background: #219ebc; color: white; margin-bottom: 10px; width: 100%; }
body.edit-mode .btn { border: 1px dashed var(--accent); }
</style>
</head>
<body>
<header>
<div id="status">MODE LECTURE</div>
<button onclick="toggleEditMode()" id="toggleBtn" style="padding: 5px 15px;">ÉDITER</button>
</header>
<div class="grid" id="board"></div>
<div id="editOverlay" onclick="closeEditIfOutside(event)">
<div id="editPanel" onclick="event.stopPropagation()">
<h3 id="editTitle" style="margin-top:0">Paramètres du bouton</h3>
<div class="form-group">
<label>Nom du bouton</label>
<input type="text" id="inputName" placeholder="Entrez un nom...">
</div>
<div class="form-group">
<button class="btn-file" onclick="document.getElementById('fileInput').click()">📁 CHOISIR UN FICHIER SON</button>
<small id="fileNameLabel" style="color:#666; display:block"></small>
</div>
<div class="form-group" style="display:flex; align-items:center; gap:10px;">
<input type="checkbox" id="inputLoop" style="width:20px; height:20px;">
<label for="inputLoop" style="margin:0">Activer la boucle (Loop)</label>
</div>
<div class="form-group">
<label>Volume</label>
<input type="range" id="inputVolume" min="0" max="1" step="0.1" value="1">
</div>
<div class="actions">
<button class="btn-cancel" onclick="closeEdit()">ANNULER</button>
<button class="btn-save" onclick="saveSettings()">ENREGISTRER</button>
</div>
</div>
</div>
<input type="file" id="fileInput" accept="audio/*" style="display:none">
<script>
let isEditMode = false;
let editingIndex = null;
let tempFileData = null;
// Données des boutons
let btnData = JSON.parse(localStorage.getItem('sb_pro_data')) || Array(30).fill(null).map((_, i) => ({
id: i, name: "", file: null, loop: false, volume: 1
}));
const players = {};
function init() {
const board = document.getElementById('board');
board.innerHTML = "";
btnData.forEach((data, i) => {
const div = document.createElement('div');
div.className = `btn ${data.file ? 'active' : ''} ${data.loop ? 'has-loop' : ''}`;
div.id = `btn-${i}`;
div.innerHTML = `<span>${data.name || (i+1)}</span>`;
div.onclick = () => handleBtnClick(i);
board.appendChild(div);
});
}
function toggleEditMode() {
isEditMode = !isEditMode;
document.body.classList.toggle('edit-mode', isEditMode);
document.getElementById('status').innerText = isEditMode ? "MODE ÉDITION" : "MODE LECTURE";
document.getElementById('toggleBtn').innerText = isEditMode ? "QUITTER" : "ÉDITER";
}
function handleBtnClick(i) {
if (isEditMode) {
openEdit(i);
} else {
playSound(i);
}
}
// --- LOGIQUE LECTURE ---
function playSound(i) {
const data = btnData[i];
if (!data.file) return;
if (players[i] && !players[i].paused) {
players[i].pause();
players[i].currentTime = 0;
document.getElementById(`btn-${i}`).classList.remove('playing');
} else {
if (!players[i]) players[i] = new Audio(data.file);
players[i].loop = data.loop;
players[i].volume = data.volume || 1;
players[i].play();
document.getElementById(`btn-${i}`).classList.add('playing');
players[i].onended = () => { if(!data.loop) document.getElementById(`btn-${i}`).classList.remove('playing'); };
}
}
// --- LOGIQUE ÉDITION ---
function openEdit(i) {
editingIndex = i;
const data = btnData[i];
document.getElementById('editTitle').innerText = "Bouton " + (i + 1);
document.getElementById('inputName').value = data.name;
document.getElementById('inputLoop').checked = data.loop;
document.getElementById('inputVolume').value = data.volume || 1;
document.getElementById('fileNameLabel').innerText = data.file ? "Fichier chargé" : "Aucun fichier";
tempFileData = data.file;
document.getElementById('editOverlay').classList.add('show');
}
document.getElementById('fileInput').onchange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (ev) => {
tempFileData = ev.target.result;
document.getElementById('fileNameLabel').innerText = "Nouveau fichier : " + file.name;
};
reader.readAsDataURL(file);
}
};
function saveSettings() {
btnData[editingIndex].name = document.getElementById('inputName').value;
btnData[editingIndex].loop = document.getElementById('inputLoop').checked;
btnData[editingIndex].volume = parseFloat(document.getElementById('inputVolume').value);
btnData[editingIndex].file = tempFileData;
localStorage.setItem('sb_pro_data', JSON.stringify(btnData));
if (players[editingIndex]) delete players[editingIndex]; // Reset audio pour appliquer changements
closeEdit();
init();
}
function closeEdit() { document.getElementById('editOverlay').classList.remove('show'); }
function closeEditIfOutside(e) { if(e.target.id === 'editOverlay') closeEdit(); }
init();
</script>
</body>
</html>