diff --git a/docs/changelog.md b/docs/changelog.md index 7384e48..fe59f61 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,29 @@ # Changelog - SimpleConnect Electron +## [1.2.12] - 2025-09-04 + +### Ajouté +- **Redimensionnement du panneau de notes** : Possibilité d'ajuster la largeur + - Poignée de redimensionnement sur le bord gauche du panneau + - Indicateur visuel permanent (3 points verticaux) + - Largeur minimale : 280px, maximale : 600px + - Sauvegarde automatique de la largeur préférée dans localStorage + - Restauration de la largeur à la réouverture + +### Amélioré +- **Fluidité du redimensionnement** : Optimisations pour une meilleure performance + - Utilisation de requestAnimationFrame pour 60 FPS + - Overlay invisible pendant le drag pour capturer tous les mouvements + - Désactivation des transitions CSS pendant le redimensionnement + - Indicateur visuel toujours visible (pas seulement au survol) + - Changement de couleur de l'indicateur au survol (gris → violet) + +### Technique +- Variable CSS `--notes-width` pour synchroniser panneau et webview +- Classes `.resizing` pour désactiver les transitions pendant le drag +- Gestion des événements mouse avec requestAnimationFrame +- Limites de taille avec Math.min/max pour contraindre la largeur + ## [1.2.11] - 2025-09-04 ### Ajouté diff --git a/index.html b/index.html index 26c5e67..6ba8a01 100644 --- a/index.html +++ b/index.html @@ -105,6 +105,7 @@
+

Notes rapides

diff --git a/renderer.js b/renderer.js index 8e63731..2b96702 100644 --- a/renderer.js +++ b/renderer.js @@ -11,6 +11,11 @@ let callStats = { appointments: 0 }; +// Variables pour le redimensionnement des notes +let isResizingNotes = false; +let notesStartWidth = 380; +let notesStartX = 0; + // === GESTION DE LA CONNEXION === document.addEventListener('DOMContentLoaded', async () => { // Initialiser l'indicateur SignalR @@ -90,6 +95,9 @@ document.addEventListener('DOMContentLoaded', async () => { // Charger les préférences utilisateur loadUserPreferences(); + // Initialiser le redimensionnement des notes + initNotesResize(); + // Gérer les boutons de la modal de déconnexion (si on est sur la page principale) const cancelLogoutBtn = document.getElementById('cancelLogoutBtn'); @@ -860,6 +868,16 @@ function showNotes() { const toggleBtn = document.getElementById('toggleNotesBtn'); const webviewContainer = document.getElementById('webviewContainer'); + // Restaurer la largeur sauvegardée + const savedWidth = localStorage.getItem('notesWidth'); + if (savedWidth && notesSection) { + const width = parseInt(savedWidth); + if (width >= 280 && width <= 600) { + notesSection.style.width = `${width}px`; + document.documentElement.style.setProperty('--notes-width', `${width}px`); + } + } + notesSection.classList.add('visible'); toggleBtn.classList.add('active'); @@ -915,6 +933,19 @@ function loadUserPreferences() { // L'utilisateur devra cliquer pour les afficher } + // Charger la largeur sauvegardée des notes + const savedNotesWidth = localStorage.getItem('notesWidth'); + if (savedNotesWidth) { + const width = parseInt(savedNotesWidth); + if (width >= 280 && width <= 600) { + document.documentElement.style.setProperty('--notes-width', `${width}px`); + const notesSection = document.getElementById('notesSection'); + if (notesSection) { + notesSection.style.width = `${width}px`; + } + } + } + // Charger d'autres préférences si nécessaire const lastTheme = localStorage.getItem('theme'); if (lastTheme) { @@ -924,8 +955,10 @@ function loadUserPreferences() { function saveUserPreferences() { // Cette fonction peut être étendue pour sauvegarder d'autres préférences + const notesSection = document.getElementById('notesSection'); const preferences = { - notesVisible: document.getElementById('notesSection').classList.contains('visible'), + notesVisible: notesSection.classList.contains('visible'), + notesWidth: notesSection ? notesSection.offsetWidth : 380, theme: document.body.getAttribute('data-theme') || 'light' }; @@ -934,6 +967,99 @@ function saveUserPreferences() { }); } +// === GESTION DU REDIMENSIONNEMENT DES NOTES === +function initNotesResize() { + const resizeHandle = document.getElementById('notesResizeHandle'); + const notesSection = document.getElementById('notesSection'); + const webviewContainer = document.getElementById('webviewContainer'); + + if (!resizeHandle || !notesSection) return; + + let rafId = null; + + resizeHandle.addEventListener('mousedown', (e) => { + isResizingNotes = true; + notesStartX = e.clientX; + notesStartWidth = notesSection.offsetWidth; + + // Ajouter les classes pour désactiver les transitions + notesSection.classList.add('resizing'); + if (webviewContainer) { + webviewContainer.classList.add('resizing'); + } + + // Empêcher la sélection de texte pendant le redimensionnement + document.body.style.userSelect = 'none'; + document.body.style.cursor = 'col-resize'; + + // Ajouter un overlay pour capturer tous les mouvements de souris + const overlay = document.createElement('div'); + overlay.id = 'resize-overlay'; + overlay.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 9999; cursor: col-resize;'; + document.body.appendChild(overlay); + + e.preventDefault(); + }); + + const handleMouseMove = (e) => { + if (!isResizingNotes) return; + + // Utiliser requestAnimationFrame pour la fluidité + if (rafId) cancelAnimationFrame(rafId); + + rafId = requestAnimationFrame(() => { + // Calculer la nouvelle largeur (inversé car le panneau est à droite) + const deltaX = notesStartX - e.clientX; + const newWidth = notesStartWidth + deltaX; + + // Appliquer les limites min/max + const clampedWidth = Math.min(Math.max(newWidth, 280), 600); + + // Mettre à jour la largeur avec transform pour plus de fluidité + notesSection.style.width = `${clampedWidth}px`; + + // Mettre à jour la variable CSS pour la webview + document.documentElement.style.setProperty('--notes-width', `${clampedWidth}px`); + }); + }; + + const handleMouseUp = () => { + if (!isResizingNotes) return; + + isResizingNotes = false; + + // Annuler l'animation frame en cours + if (rafId) cancelAnimationFrame(rafId); + + // Retirer l'overlay + const overlay = document.getElementById('resize-overlay'); + if (overlay) overlay.remove(); + + // Retirer les classes de redimensionnement + const notesSection = document.getElementById('notesSection'); + const webviewContainer = document.getElementById('webviewContainer'); + + if (notesSection) { + notesSection.classList.remove('resizing'); + } + if (webviewContainer) { + webviewContainer.classList.remove('resizing'); + } + + // Réactiver la sélection de texte + document.body.style.userSelect = ''; + document.body.style.cursor = ''; + + // Sauvegarder la nouvelle largeur + if (notesSection) { + localStorage.setItem('notesWidth', notesSection.offsetWidth); + } + }; + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); +} + // === FONCTION DE RAFRAÎCHISSEMENT === function refreshCurrentWebview() { if (activeCenter && webviews[activeCenter]) { diff --git a/styles-modern.css b/styles-modern.css index c358d7b..783acce 100644 --- a/styles-modern.css +++ b/styles-modern.css @@ -618,6 +618,8 @@ body { top: 60px; /* Juste sous le header */ right: -400px; /* Caché par défaut */ width: 380px; + min-width: 280px; + max-width: 600px; height: calc(100vh - 60px); background: white; box-shadow: -4px 0 20px rgba(0,0,0,0.1); @@ -628,6 +630,69 @@ body { border-left: 1px solid #e1e4e8; } +/* Poignée de redimensionnement */ +.notes-resize-handle { + position: absolute; + left: -5px; + top: 0; + bottom: 0; + width: 10px; + cursor: col-resize; + background: linear-gradient(90deg, + transparent 0%, + transparent 30%, + rgba(209, 213, 219, 0.5) 50%, + transparent 70%, + transparent 100%); + z-index: 10; +} + +.notes-resize-handle:hover { + background: linear-gradient(90deg, + transparent 0%, + rgba(102, 126, 234, 0.1) 30%, + rgba(102, 126, 234, 0.3) 50%, + rgba(102, 126, 234, 0.1) 70%, + transparent 100%); +} + +.notes-resize-handle:active { + background: linear-gradient(90deg, + transparent 0%, + rgba(102, 126, 234, 0.2) 30%, + rgba(102, 126, 234, 0.5) 50%, + rgba(102, 126, 234, 0.2) 70%, + transparent 100%); +} + +/* Indicateur visuel permanent - 3 points verticaux */ +.notes-resize-handle::before { + content: '⋮⋮⋮'; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) rotate(90deg); + font-size: 16px; + color: #9ca3af; + letter-spacing: -4px; + font-weight: bold; + pointer-events: none; + user-select: none; +} + +.notes-resize-handle:hover::before { + color: #667eea; +} + +/* Désactiver les transitions pendant le redimensionnement */ +.notes-section.resizing { + transition: none !important; +} + +.webview-container.resizing { + transition: none !important; +} + .notes-section.visible { right: 0; animation: slideInRight 0.3s ease-out; @@ -750,7 +815,7 @@ body { /* Ajustement de la webview quand les notes sont visibles */ .webview-container.notes-open { - margin-right: 380px; + margin-right: var(--notes-width, 380px); transition: margin-right 0.3s cubic-bezier(0.4, 0, 0.2, 1); }