Bonjour,
vibe codé avec le tout récent Claude Opus 4.6, il... s'en sort très bien. Ta demande très claire est évidemment pour beaucoup là dedans.
https://codepen.io/PhilippeVay/pen/RNRYGPB
Les contrastes pas glop, c'est pas moi...
Le panneau d'aide (raccourcis clavier) ou les logs, ben... je lui ai pas demandé.

Juste précisé "Oh, fais-en une démo complète"
EDIT : l'iso-8859-1 du Forum a comme d'hab' mangé

plein de caractères... Voir le Codepen
/**
* setupMusicInput(textarea)
*
* Écoute les frappes clavier sur un <textarea> et remplace
* automatiquement les caractères d'altération musicale :
*
* Note + # ? ? dièse (U+266F)
* Note + b ? ? bémol (U+266D)
* Note + = ? ? bécarre (U+266E)
* ? + # ? ???? double dièse (U+1D12A) — remplace le ? précédent
* ? + b ? ???? double bémol (U+1D12B) — remplace le ? précédent
*/
function setupMusicInput(textarea, logEl) {
// Le nom de note doit être précédé d'un séparateur ou être en début de texte
const notePattern = /(?:^|[\s,;. [decu])\[\]\/\-])(?:Do|Ré|Re|Mi|Fa|Sol|La|Si)$/i;
textarea.addEventListener('keydown', function (e) {
// Ne pas interférer avec les raccourcis système
if (e.ctrlKey || e.altKey || e.metaKey) return;
const pos = this.selectionStart;
const text = this.value;
const textBefore = text.substring(0, pos);
// ?? Double dièse : ? + # ? ???? ??
if (e.key === '#' && textBefore.endsWith('\u266F')) {
e.preventDefault();
replaceBack(this, pos, 1, '\uD834\uDD2A'); // ????
log(logEl, '? + #', '????', 'double dièse');
return;
}
// ?? Double bémol : ? + b ? ???? ??
if (e.key === 'b' && textBefore.endsWith('\u266D')) {
e.preventDefault();
replaceBack(this, pos, 1, '\uD834\uDD2B'); // ????
log(logEl, '? + b', '????', 'double bémol');
return;
}
// ?? Dièse : Note + # ? ? ??
if (e.key === '#' && notePattern.test(textBefore)) {
e.preventDefault();
insertChar(this, pos, '\u266F'); // ?
log(logEl, extractNote(textBefore) + ' + #', extractNote(textBefore) + '?', 'dièse');
return;
}
// ?? Bémol : Note + b ? ? ??
if (e.key === 'b' && notePattern.test(textBefore)) {
e.preventDefault();
insertChar(this, pos, '\u266D'); // ?
log(logEl, extractNote(textBefore) + ' + b', extractNote(textBefore) + '?', 'bémol');
return;
}
// ?? Bécarre : Note + = ? ? ??
if (e.key === '=' && notePattern.test(textBefore)) {
e.preventDefault();
insertChar(this, pos, '\u266E'); // ?
log(logEl, extractNote(textBefore) + ' + =', extractNote(textBefore) + '?', 'bécarre');
return;
}
});
/** Insère un caractère à la position du curseur */
function insertChar(el, pos, char) {
const before = el.value.substring(0, pos);
const after = el.value.substring(el.selectionEnd);
el.value = before + char + after;
el.selectionStart = el.selectionEnd = pos + char.length;
el.dispatchEvent(new Event('input', { bubbles: true }));
}
/** Remplace `count` caractères avant le curseur par `char` */
function replaceBack(el, pos, count, char) {
const before = el.value.substring(0, pos - count);
const after = el.value.substring(el.selectionEnd);
el.value = before + char + after;
el.selectionStart = el.selectionEnd = pos - count + char.length;
el.dispatchEvent(new Event('input', { bubbles: true }));
}
/** Extrait le nom de note à la fin du texte */
function extractNote(text) {
const m = text.match(/(?:Do|Ré|Re|Mi|Fa|Sol|La|Si)$/i);
return m ? m[0] : '';
}
/** Ajoute une entrée dans le journal */
function log(el, from, to, label) {
if (!el) return;
const entry = document.createElement('div');
entry.className = 'entry';
entry.innerHTML =
'<span class="highlight">' + escHtml(from) + '</span>' +
' ? ' +
'<span class="highlight">' + escHtml(to) + '</span>' +
' <span style="color:#556677">(' + escHtml(label) + ')</span>';
el.prepend(entry);
}
function escHtml(s) {
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
}
}
// ?? Initialisation ??
const textarea = document.getElementById('music-notes');
const logEl = document.getElementById('log');
setupMusicInput(textarea, logEl);
Modifié par Felipe (06 Feb 2026 - 12:53)