fix: volume slider par alerte, défaut 10%, suppression du pre-cache au démarrage
- Chaque alerte a son propre slider volume (0–100%, step 1, défaut 10%) - Volume très bas possible (0%) — pas de minimum forcé - Slider volume aussi dans le formulaire d'ajout pour setter dès la création - Supprime le pre-cache IndexedDB au démarrage qui ralentissait le lancement - Supprime le slider volume global (remplacé par le per-alerte) - Migration automatique des anciennes alertes vers volume=10 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -748,19 +748,19 @@
|
|||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-head-row">
|
<div class="panel-head-row">
|
||||||
<h2>Alertes de drop</h2>
|
<h2>Alertes de drop</h2>
|
||||||
<div style="display:flex;align-items:center;gap:8px;margin-left:auto;">
|
|
||||||
<span style="font-size:11px;color:var(--text-dim);">Volume</span>
|
|
||||||
<input type="range" id="alertGlobalVolume" min="0" max="100" step="5" value="25" style="width:80px;">
|
|
||||||
<span id="alertGlobalVolumeVal" style="font-size:10px;font-family:var(--font-mono);color:var(--text-dim);min-width:28px;text-align:right;">25%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="timer-add-row">
|
<div class="timer-add-row" style="flex-wrap:wrap;gap:6px;">
|
||||||
<input type="text" id="alertItemInput" placeholder="Nom de l'item (ex: Diamond)" style="flex:2;" list="alertItemSuggestions" autocomplete="off">
|
<input type="text" id="alertItemInput" placeholder="Nom de l'item (ex: Diamond)" style="flex:2;min-width:140px;" list="alertItemSuggestions" autocomplete="off">
|
||||||
<datalist id="alertItemSuggestions"></datalist>
|
<datalist id="alertItemSuggestions"></datalist>
|
||||||
<select id="alertSoundSelect" class="alert-cfg-sound"></select>
|
<select id="alertSoundSelect" class="alert-cfg-sound"></select>
|
||||||
|
<div style="display:flex;align-items:center;gap:5px;">
|
||||||
|
<span style="font-size:10px;color:var(--text-dim);">Vol</span>
|
||||||
|
<input type="range" id="alertVolumeInput" min="0" max="100" step="1" value="10" style="width:64px;">
|
||||||
|
<span id="alertVolumeVal" style="font-size:10px;font-family:var(--font-mono);color:var(--text-dim);min-width:26px;">10%</span>
|
||||||
|
</div>
|
||||||
<button class="btn-ghost" id="alertAddBtn">Ajouter</button>
|
<button class="btn-ghost" id="alertAddBtn">Ajouter</button>
|
||||||
</div>
|
</div>
|
||||||
<p style="font-size:11.5px;color:var(--text-dim);margin:6px 0 4px;">Son joué à chaque fois que cet item drop. Volume global s'applique à toutes les alertes.</p>
|
<p style="font-size:11.5px;color:var(--text-dim);margin:6px 0 4px;">Chaque alerte a son propre son et volume.</p>
|
||||||
<div id="alertConfigList"></div>
|
<div id="alertConfigList"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2313,14 +2313,10 @@
|
|||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlertVolume(){
|
|
||||||
return parseInt(localStorage.getItem('alertGlobalVolume') || '25', 10) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function playAlert(cfg){
|
async function playAlert(cfg){
|
||||||
try {
|
try {
|
||||||
const soundId = cfg ? (cfg.soundId || 'levelup') : 'levelup';
|
const soundId = cfg ? (cfg.soundId || 'levelup') : 'levelup';
|
||||||
const vol = getAlertVolume();
|
const vol = Math.max(0, Math.min(1, ((cfg && cfg.volume != null) ? cfg.volume : 10) / 100));
|
||||||
const buf = await _getDecodedBuffer(soundId);
|
const buf = await _getDecodedBuffer(soundId);
|
||||||
if (!buf){ console.warn('Sound not cached:', soundId); return; }
|
if (!buf){ console.warn('Sound not cached:', soundId); return; }
|
||||||
const ctx = getAudioCtx();
|
const ctx = getAudioCtx();
|
||||||
@@ -2329,23 +2325,20 @@
|
|||||||
src.buffer = buf;
|
src.buffer = buf;
|
||||||
src.connect(gain);
|
src.connect(gain);
|
||||||
gain.connect(ctx.destination);
|
gain.connect(ctx.destination);
|
||||||
gain.gain.value = Math.max(0, Math.min(1, vol));
|
gain.gain.value = vol;
|
||||||
src.start();
|
src.start();
|
||||||
} catch(e){ console.warn('playAlert failed', e); }
|
} catch(e){ console.warn('playAlert failed', e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// migration ancien format (sans soundId)
|
// migration : ajoute soundId et volume si absents
|
||||||
let alertConfigs = JSON.parse(localStorage.getItem('alertConfigs') || '[]').map(c => ({
|
let alertConfigs = JSON.parse(localStorage.getItem('alertConfigs') || '[]').map(c => ({
|
||||||
soundId: 'levelup', ...c
|
soundId: 'levelup', volume: 10, ...c
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function saveAlertConfigs(){
|
function saveAlertConfigs(){
|
||||||
localStorage.setItem('alertConfigs', JSON.stringify(alertConfigs));
|
localStorage.setItem('alertConfigs', JSON.stringify(alertConfigs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-cache levelup.ogg au démarrage (son par défaut)
|
|
||||||
_getDecodedBuffer('levelup').catch(() => {});
|
|
||||||
|
|
||||||
// activeTimers : Map<configId, { endsAt: number, durationSec: number, mobName: string }>
|
// activeTimers : Map<configId, { endsAt: number, durationSec: number, mobName: string }>
|
||||||
const activeTimers = new Map();
|
const activeTimers = new Map();
|
||||||
let timerClockId = null;
|
let timerClockId = null;
|
||||||
@@ -2513,9 +2506,12 @@
|
|||||||
const label = s.label + (notCached ? ' (⬇)' : '');
|
const label = s.label + (notCached ? ' (⬇)' : '');
|
||||||
return '<option value="'+escHtml(s.id)+'"'+(s.id === sid ? ' selected' : '')+'>'+escHtml(label)+'</option>';
|
return '<option value="'+escHtml(s.id)+'"'+(s.id === sid ? ' selected' : '')+'>'+escHtml(label)+'</option>';
|
||||||
}).join('');
|
}).join('');
|
||||||
|
const vol = cfg.volume != null ? cfg.volume : 10;
|
||||||
return '<div class="alert-cfg-row">'+
|
return '<div class="alert-cfg-row">'+
|
||||||
'<span class="alert-cfg-name">'+escHtml(cfg.itemName)+'</span>'+
|
'<span class="alert-cfg-name">'+escHtml(cfg.itemName)+'</span>'+
|
||||||
'<select class="alert-cfg-sound" data-alert-sound="'+escHtml(cfg.id)+'">'+opts+'</select>'+
|
'<select class="alert-cfg-sound" data-alert-sound="'+escHtml(cfg.id)+'">'+opts+'</select>'+
|
||||||
|
'<input type="range" min="0" max="100" step="1" value="'+vol+'" data-alert-vol="'+escHtml(cfg.id)+'" style="width:64px;" title="Volume: '+vol+'%">'+
|
||||||
|
'<span data-alert-vol-label="'+escHtml(cfg.id)+'" style="font-size:10px;font-family:var(--font-mono);color:var(--text-dim);min-width:26px;">'+vol+'%</span>'+
|
||||||
'<button class="btn-ghost small" data-preview-alert="'+escHtml(cfg.id)+'" title="Prévisualiser">▶</button>'+
|
'<button class="btn-ghost small" data-preview-alert="'+escHtml(cfg.id)+'" title="Prévisualiser">▶</button>'+
|
||||||
'<button class="btn-ghost small danger" data-del-alert="'+escHtml(cfg.id)+'">✕</button>'+
|
'<button class="btn-ghost small danger" data-del-alert="'+escHtml(cfg.id)+'">✕</button>'+
|
||||||
'</div>';
|
'</div>';
|
||||||
@@ -2550,16 +2546,21 @@
|
|||||||
sel.innerHTML = buildSoundOptions('levelup');
|
sel.innerHTML = buildSoundOptions('levelup');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// slider volume dans le formulaire d'ajout
|
||||||
|
$('alertVolumeInput').addEventListener('input', () => {
|
||||||
|
$('alertVolumeVal').textContent = $('alertVolumeInput').value + '%';
|
||||||
|
});
|
||||||
|
|
||||||
$('alertAddBtn').addEventListener('click', () => {
|
$('alertAddBtn').addEventListener('click', () => {
|
||||||
const itemName = $('alertItemInput').value.trim();
|
const itemName = $('alertItemInput').value.trim();
|
||||||
if (!itemName) return;
|
if (!itemName) return;
|
||||||
const soundId = $('alertSoundSelect').value || 'levelup';
|
const soundId = $('alertSoundSelect').value || 'levelup';
|
||||||
alertConfigs.push({ id: generateUUID(), itemName, nameLower: itemName.toLowerCase(), soundId });
|
const volume = parseInt($('alertVolumeInput').value, 10);
|
||||||
|
alertConfigs.push({ id: generateUUID(), itemName, nameLower: itemName.toLowerCase(), soundId, volume });
|
||||||
saveAlertConfigs();
|
saveAlertConfigs();
|
||||||
$('alertItemInput').value = '';
|
$('alertItemInput').value = '';
|
||||||
renderAlertConfigList();
|
renderAlertConfigList();
|
||||||
populateAlertItemSuggestions();
|
populateAlertItemSuggestions();
|
||||||
// auto-download si son pas encore en cache
|
|
||||||
if (soundId !== 'levelup') isSoundCached(soundId).then(ok => { if (!ok) downloadSound(soundId).catch(()=>{}); });
|
if (soundId !== 'levelup') isSoundCached(soundId).then(ok => { if (!ok) downloadSound(soundId).catch(()=>{}); });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2592,23 +2593,26 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('alertConfigList').addEventListener('change', e => {
|
$('alertConfigList').addEventListener('change', e => {
|
||||||
const id = e.target.dataset.alertSound;
|
const soundId = e.target.dataset.alertSound;
|
||||||
|
if (soundId){
|
||||||
|
const cfg = alertConfigs.find(c => c.id === soundId);
|
||||||
|
if (!cfg) return;
|
||||||
|
cfg.soundId = e.target.value;
|
||||||
|
saveAlertConfigs();
|
||||||
|
if (cfg.soundId !== 'levelup') isSoundCached(cfg.soundId).then(ok => { if (!ok) downloadSound(cfg.soundId).catch(()=>{}); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('alertConfigList').addEventListener('input', e => {
|
||||||
|
const id = e.target.dataset.alertVol;
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
const cfg = alertConfigs.find(c => c.id === id);
|
const cfg = alertConfigs.find(c => c.id === id);
|
||||||
if (!cfg) return;
|
if (!cfg) return;
|
||||||
cfg.soundId = e.target.value;
|
cfg.volume = parseInt(e.target.value, 10);
|
||||||
saveAlertConfigs();
|
saveAlertConfigs();
|
||||||
if (cfg.soundId !== 'levelup') isSoundCached(cfg.soundId).then(ok => { if (!ok) downloadSound(cfg.soundId).catch(()=>{}); });
|
const label = document.querySelector('[data-alert-vol-label="'+id+'"]');
|
||||||
});
|
if (label) label.textContent = cfg.volume + '%';
|
||||||
|
|
||||||
// ---- Volume global ----
|
|
||||||
const _volSlider = $('alertGlobalVolume');
|
|
||||||
const _volLabel = $('alertGlobalVolumeVal');
|
|
||||||
const _savedVol = localStorage.getItem('alertGlobalVolume') || '25';
|
|
||||||
if (_volSlider){ _volSlider.value = _savedVol; _volLabel.textContent = _savedVol + '%'; }
|
|
||||||
_volSlider && _volSlider.addEventListener('input', () => {
|
|
||||||
localStorage.setItem('alertGlobalVolume', _volSlider.value);
|
|
||||||
_volLabel.textContent = _volSlider.value + '%';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---- Sound library ----
|
// ---- Sound library ----
|
||||||
|
|||||||
Reference in New Issue
Block a user