V1.008 Evolution des paramétrages de l'écran de fond

This commit is contained in:
2026-09-03 08:17:15 +00:00
parent 7120d4d65e
commit d5be94c4d8
7 changed files with 370 additions and 10 deletions
+263
View File
@@ -177,6 +177,47 @@
opaque (au lieu d'une transparence à 62%) à la demande de Jean-Marc —
reste plus sobre que les objets fonctionnels, mais plus affirmée
qu'avant. */
/* Pastille "nombre de contacts" sur le classeur Contacts (2 septembre
2026, essai). Idée de Jean-Marc : au fur et à mesure que chaque classeur
sera branché à sa table, afficher une petite pastille collée dessous le
trou cerclé de métal, avec un chiffre — pour donner une indication
immédiate du contenu sans avoir à ouvrir le classeur. Couleur de fond
mesurée sur ecran_fond.jpg (teinte réelle du classeur "Contacts",
rgb≈3,79,99, plus sombre en photo qu'à l'écran) puis assombrie d'un
facteur ~0.55 pour que le chiffre blanc ressorte sans jurer avec le
classeur — pas une couleur arbitraire. Liseré blanc autour de la
pastille (2 fichiers/gris clair, cohérent avec le liseré déjà utilisé
sur les étiquettes de tous les classeurs sur la photo) + légère ombre
portée pour la détacher visuellement, plutôt qu'un anneau de couleur :
Jean-Marc n'avait pas de préférence tranchée sur la couleur de l'anneau,
le blanc a été choisi pour rester cohérent avec l'habillage existant
(à changer facilement si Jean-Marc préfère une autre couleur après test
réel). pointer-events:none : la pastille est purement informative, le
clic doit continuer à passer à travers vers #classeur-contacts en
dessous (même logique que #micro-icone au-dessus de #micro-desk). Le
pattern (variables IMG, positionnement dans positionner(), chargement
via un endpoint réel) est pensé pour être répliqué sur les prochains
classeurs au fur et à mesure de leur branchement — voir commentaire de
PASTILLE_CONTACTS_* plus bas dans le script. Retouchée le même jour
après premier test réel de Jean-Marc ("trop présente, liseré trop
pétant") : diamètre réduit (22px de référence → 17px) et liseré blanc
assoupli (opacité .85→.4, épaisseur 2px→1.5px) pour rester une
indication discrète plutôt qu'un élément qui attire l'œil en premier. */
#pastille-contacts{
position:fixed;
border-radius:50%;
background:#022b36;
box-shadow:0 0 0 1.5px rgba(255,255,255,.4), 0 1px 2px rgba(0,0,0,.45);
display:flex;align-items:center;justify-content:center;
pointer-events:none;
user-select:none;
}
#pastille-contacts span{
color:#fff;
font-family:"Segoe UI","Arial",sans-serif;
font-weight:700;
line-height:1;
}
#version-desk{
position:fixed;
color:#3a2712;
@@ -188,6 +229,65 @@
pointer-events:none;
user-select:none;
}
/* Post-its de la ToDoList personnelle (2 septembre 2026, premier
affichage réel — voir journal section 28). Posés dans la zone décrite
par ZONE_POSTITS_* plus bas dans le script : une bande occupant 30% de
la largeur de l'écran depuis le bord droit, juste au-dessus du chevalet
— décision de Jean-Marc ("le vilain petit canard sera l'humain", donc
plus de place que chaque assistant, dont la ToDoList n'est pas encore
gérée — voir Réflexions stratégiques section 11 et le fil du 2-3
septembre). Grille à 4 colonnes × 5 lignes = 20 cases, volontairement
alignée sur CAPACITE_TABLEAU_PAR_UTILISATEUR (todolist.php) pour cette
première version — les deux devront évoluer ensemble si la capacité
change. Chaque post-it reçoit une légère rotation/décalage à
l'intérieur de sa propre case (jamais à cheval sur la case voisine,
donc jamais de chevauchement ce soir — Jean-Marc a autorisé le
chevauchement partiel mais a refusé l'empilement en tas ; rester dans
sa case est la façon la plus simple de garantir qu'on ne recrée jamais
ce tas). Décalage/rotation calculés de façon déterministe à partir de
TDL_Incr (voir hashDeterministe() dans le script) : la disposition ne
"saute" pas à chaque rechargement de l'écran.
Couleur jaune classique pour l'utilisateur (--postit-jaune) — les
couleurs par assistant restent à faire le jour où leur ToDoList sera
gérée (AGN_Couleur, voir Réflexions stratégiques section 11).
Punaise ronde en haut (photo confirmée comme du vrai liège par
Jean-Marc le 2 septembre) plutôt qu'un simple post-it posé — cohérent
avec la matière réelle du fond d'écran. Pastille d'urgence toujours
dans le coin haut-droit, jamais recouverte par du jitter (voir calcul
de bornes dans positionnerPostits()) : c'est le repère visuel ET la
garantie d'une zone de clic non ambiguë même si deux post-its finissent
visuellement proches. Pas de texte sur le post-it (choix explicite de
Jean-Marc) : le titre de la tâche n'apparaît qu'au survol (title natif),
le clic ne fait rien pour l'instant (voir journal — navigation vers
liste_todolist.html volontairement pas encore branchée). */
.postit{
position:fixed;
box-shadow:2px 4px 8px rgba(0,0,0,.35);
background:
linear-gradient(135deg, transparent 50%, rgba(0,0,0,.12) 50%, rgba(0,0,0,.12) 62%, transparent 62%) bottom right / 22% 22% no-repeat,
var(--couleur-postit, #fbe14a);
cursor:default;
}
.postit::before{
content:'';
position:absolute;
top:-9px; left:50%;
transform:translateX(-50%);
width:13%; height:13%;
min-width:8px; min-height:8px;
border-radius:50%;
background:radial-gradient(circle at 35% 30%, #fff8, transparent 40%), #8a3b12;
box-shadow:0 2px 3px rgba(0,0,0,.5);
}
.postit .pastille-urgence-postit{
position:absolute;
top:-15%; right:-15%;
width:32%; height:32%;
border-radius:50%;
box-shadow:0 0 0 2px rgba(255,255,255,.9), 0 1px 2px rgba(0,0,0,.4);
}
.postit .pastille-urgence-postit.rouge{ background:#c0392b; }
.postit .pastille-urgence-postit.orange{ background:#ff5a1f; }
</style>
</head>
<body>
@@ -210,6 +310,7 @@
<rect class="barre" x="20" y="11" width="3.5" height="10" rx="1"/>
</svg>
</div>
<div id="pastille-contacts"><span id="pastille-contacts-nombre">…</span></div>
<div id="version-desk"></div>
<script>
@@ -264,6 +365,45 @@
// calé pour se terminer avant, vers y=436.
const VERSION_X = 1012, VERSION_Y = 424;
const VERSION_POLICE_FRAC = 11 / IMG_W;
// Pastille "nombre de contacts", sous le trou cerclé de métal du classeur
// Contacts. Trou mesuré sur ecran_fond.jpg : centre (58, 354), anneau
// métallique jusqu'à y≈365. Pastille centrée sur le même axe X que le
// trou (cohérence visuelle), posée juste en dessous, avant la fin du
// classeur (bas de la tranche à y=397, voir CLASSEUR_Y2). Diamètre réduit
// de 22 à 17 le 2 septembre 2026 (retour de Jean-Marc : "trop présente") —
// centre recalé à (58, 381) pour rester au milieu de l'espace disponible
// entre l'anneau et le bas du classeur avec la nouvelle taille.
const PASTILLE_CONTACTS_CENTRE_X = 58, PASTILLE_CONTACTS_CENTRE_Y = 381;
const PASTILLE_CONTACTS_DIAM_FRAC = 17 / IMG_W;
// Zone des post-its personnels (2 septembre 2026, premier affichage réel
// — voir commentaire CSS .postit ci-dessus pour le détail des décisions).
// 30% de la largeur de l'écran depuis le bord droit (1024 × 0.30 ≈ 307),
// du haut de l'image jusqu'à juste au-dessus du chevalet (CHEVALET_Y1 =
// 362, marge de 10px). Grille 4 colonnes × 5 lignes = 20 cases, alignée
// sur CAPACITE_TABLEAU_PAR_UTILISATEUR (todolist.php) — à faire évoluer
// ensemble le jour où cette capacité changera.
const ZONE_POSTITS_X1 = 717, ZONE_POSTITS_X2 = 1024;
const ZONE_POSTITS_Y1 = 6, ZONE_POSTITS_Y2 = 352;
const GRILLE_POSTITS_COLONNES = 4, GRILLE_POSTITS_LIGNES = 5;
// Taille d'un post-it : ~60% de la plus petite dimension de case, pour
// laisser de la marge au jitter sans jamais déborder sur la case voisine
// (voir positionnerPostits()).
const POSTIT_RATIO_CASE = 0.6;
/**
* Petit générateur pseudo-aléatoire déterministe (0..1), à partir d'un
* entier (TDL_Incr) — pour que le jitter (rotation/décalage) d'un post-it
* donné reste toujours le même d'un rechargement à l'autre de l'écran,
* plutôt que de "sauter" à chaque fois (mulberry32, implémentation
* minimale, largement suffisante ici — pas besoin de qualité crypto).
*/
function pseudoAleatoire(graine) {
let t = (graine + 0x6D2B79F5) | 0;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
}
function positionner() {
const vw = window.innerWidth, vh = window.innerHeight;
@@ -313,6 +453,49 @@
versionDesk.style.width = '200px';
versionDesk.style.top = (offY + (VERSION_Y / IMG_H) * dispH) + 'px';
versionDesk.style.fontSize = (VERSION_POLICE_FRAC * dispW) + 'px';
const pastilleContacts = document.getElementById('pastille-contacts');
const pastilleDiam = PASTILLE_CONTACTS_DIAM_FRAC * dispW;
pastilleContacts.style.left = (offX + (PASTILLE_CONTACTS_CENTRE_X / IMG_W) * dispW - pastilleDiam / 2) + 'px';
pastilleContacts.style.top = (offY + (PASTILLE_CONTACTS_CENTRE_Y / IMG_H) * dispH - pastilleDiam / 2) + 'px';
pastilleContacts.style.width = pastilleDiam + 'px';
pastilleContacts.style.height = pastilleDiam + 'px';
// Taille de police proportionnelle au diamètre, réduite si le nombre a
// 3 chiffres ou plus pour continuer à tenir dans le rond sans déborder.
const nbChiffres = document.getElementById('pastille-contacts-nombre').textContent.length;
const facteurPolice = nbChiffres >= 4 ? 0.34 : (nbChiffres === 3 ? 0.42 : 0.52);
document.getElementById('pastille-contacts-nombre').style.fontSize = (pastilleDiam * facteurPolice) + 'px';
// Post-its personnels : chaque .postit porte sa case (data-colonne/
// data-ligne, attribuées au chargement — voir creerPostits()) et une
// graine (data-graine = TDL_Incr) pour un jitter stable d'un
// rechargement à l'autre. Le jitter reste borné à l'intérieur de la
// case (marge × 0.8, jamais 1) pour ne jamais déborder sur la case
// voisine — c'est ce qui garantit l'absence d'empilement.
const zoneW = ((ZONE_POSTITS_X2 - ZONE_POSTITS_X1) / IMG_W) * dispW;
const zoneH = ((ZONE_POSTITS_Y2 - ZONE_POSTITS_Y1) / IMG_H) * dispH;
const caseW = zoneW / GRILLE_POSTITS_COLONNES;
const caseH = zoneH / GRILLE_POSTITS_LIGNES;
const postitTaille = Math.min(caseW, caseH) * POSTIT_RATIO_CASE;
const zoneLeft = offX + (ZONE_POSTITS_X1 / IMG_W) * dispW;
const zoneTop = offY + (ZONE_POSTITS_Y1 / IMG_H) * dispH;
const margeX = (caseW - postitTaille) / 2 * 0.8;
const margeY = (caseH - postitTaille) / 2 * 0.8;
document.querySelectorAll('.postit').forEach(function (postit) {
const col = Number(postit.dataset.colonne);
const ligne = Number(postit.dataset.ligne);
const graine = Number(postit.dataset.graine);
const caseLeft = zoneLeft + col * caseW;
const caseTop = zoneTop + ligne * caseH;
const decalageX = (pseudoAleatoire(graine) * 2 - 1) * margeX;
const decalageY = (pseudoAleatoire(graine + 1) * 2 - 1) * margeY;
const rotation = (pseudoAleatoire(graine + 2) * 2 - 1) * 7; // ±7°
postit.style.width = postitTaille + 'px';
postit.style.height = postitTaille + 'px';
postit.style.left = (caseLeft + (caseW - postitTaille) / 2 + decalageX) + 'px';
postit.style.top = (caseTop + (caseH - postitTaille) / 2 + decalageY) + 'px';
postit.style.transform = 'rotate(' + rotation.toFixed(1) + 'deg)';
});
}
window.addEventListener('resize', positionner);
positionner();
@@ -381,6 +564,86 @@
}
chargerVersionDesk();
// Pastille "nombre de contacts" du classeur Contacts : total réel de la
// table (contacts.php?compte=1, voir journal section 28 et commentaire
// ajouté dans contacts.php), jamais un chiffre figé. Repli visuel "—" en
// cas d'échec (même logique que chargerIdentiteChevalet), pour ne jamais
// laisser une pastille vide sans explication. Le pattern (constantes
// PASTILLE_*, positionnement dans positionner(), chargement ici) est
// pensé pour être dupliqué sur les prochains classeurs à mesure qu'ils
// seront branchés à leur table.
async function chargerCompteContacts() {
const elNombre = document.getElementById('pastille-contacts-nombre');
try {
const reponse = await fetch('contacts.php?compte=1');
if (!reponse.ok) throw new Error('HTTP ' + reponse.status);
const data = await reponse.json();
elNombre.textContent = (typeof data.total === 'number') ? String(data.total) : '—';
} catch (erreur) {
console.error('Erreur de chargement de contacts.php?compte=1 (pastille classeur) :', erreur);
elNombre.textContent = '—';
}
positionner(); // recalcule la taille de police selon le nombre réel de chiffres
}
chargerCompteContacts();
// Post-its personnels (2 septembre 2026, premier affichage réel — voir
// commentaire CSS .postit et constantes ZONE_POSTITS_*/GRILLE_POSTITS_*
// plus haut). Source : todolist.php (déjà utilisé par liste_todolist.html,
// voir journal section 26), filtré ici côté client sur l'utilisateur
// connecté (TDL_ToDoUser) et sur les tâches non réalisées (TDL_RealDate
// vide) — l'endpoint reste volontairement non filtré par utilisateur
// (voir son commentaire d'en-tête), donc le tri "c'est MES post-its" se
// fait ici. L'API renvoie déjà les tâches triées par urgence (rouge avant
// orange avant vert, voir calculerGroupe()) : cet ordre est conservé pour
// remplir la grille case par case, donc les plus urgentes arrivent
// toujours en premier (en haut à gauche de la zone). Une tâche déjà
// réalisée ne crée jamais de post-it : elle libère sa place sur le
// tableau dès qu'elle est marquée faite (voir aussi CAPACITE_TABLEAU_
// PAR_UTILISATEUR dans todolist.php — même logique des deux côtés).
// Coupé à la capacité de la grille par sécurité (ne devrait normalement
// jamais arriver, la capacité étant déjà appliquée à la création côté
// todolist.php).
async function chargerMesPostits() {
try {
const [reponsePrefs, reponseTaches] = await Promise.all([
fetch('preferences.php'),
fetch('todolist.php'),
]);
if (!reponsePrefs.ok) throw new Error('HTTP ' + reponsePrefs.status + ' (preferences.php)');
if (!reponseTaches.ok) throw new Error('HTTP ' + reponseTaches.status + ' (todolist.php)');
const prefs = await reponsePrefs.json();
const dataTaches = await reponseTaches.json();
const capacite = GRILLE_POSTITS_COLONNES * GRILLE_POSTITS_LIGNES;
const mesTaches = (dataTaches.taches || [])
.filter(t => t.TDL_ToDoUser === prefs.USR_Code && !t.TDL_RealDate)
.slice(0, capacite);
mesTaches.forEach((tache, index) => {
const postit = document.createElement('div');
postit.className = 'postit';
postit.style.setProperty('--couleur-postit', '#fbe14a'); // jaune classique, toujours pour l'humain
postit.title = tache.TDL_ToDoTxt || '';
postit.dataset.colonne = index % GRILLE_POSTITS_COLONNES;
postit.dataset.ligne = Math.floor(index / GRILLE_POSTITS_COLONNES);
postit.dataset.graine = tache.TDL_Incr;
const pastille = document.createElement('div');
// TDL_Groupe 0 = rouge, 1 = orange (le vert n'atteint jamais cet
// écran, voir filtre ci-dessus sur TDL_RealDate).
pastille.className = 'pastille-urgence-postit ' + (tache.TDL_Groupe === 0 ? 'rouge' : 'orange');
postit.appendChild(pastille);
document.body.appendChild(postit);
});
positionner(); // place les post-its qui viennent d'être créés
} catch (erreur) {
console.error('Erreur de chargement des post-its personnels (todolist.php/preferences.php) :', erreur);
}
}
chargerMesPostits();
// Lien visible "→ Liste des tâches" retiré le 2 septembre 2026 à la
// demande de Jean-Marc. Échap conservé comme échappatoire silencieuse
// (même logique que le reste de l'appli), pour ne pas rester bloqué sur