BetterPronote++

Ultimate customizable theme and enhancement suite for Pronote

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         BetterPronote++
// @namespace    http://tampermonkey.net/
// @version      8.2.0
// @description  Ultimate customizable theme and enhancement suite for Pronote
// @author       Camille Daguin
// @license      Personal Use Only - No modifications or redistribution
// @match        https://*/pronote/*
// @match        https://*/pronote/eleve.html*
// @match        https://*.index-education.net/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-end
// ==/UserScript==

(function() {
"use strict";

// Bundled modules

// Module: settings
const predefinedThemes = {
  rose: {
    primaryColor: "#ff69b4",
    lightColor: "#ffc0cb",
    backgroundColor: "#fff5f9",
  },
  bleu: {
    primaryColor: "#4a90e2",
    lightColor: "#7fc4fd",
    backgroundColor: "#f0f8ff",
  },
  vert: {
    primaryColor: "#50c878",
    lightColor: "#90ee90",
    backgroundColor: "#f0fff0",
  },
  violet: {
    primaryColor: "#9370db",
    lightColor: "#dda0dd",
    backgroundColor: "#faf0ff",
  },
  orange: {
    primaryColor: "#ff8c00",
    lightColor: "#ffd700",
    backgroundColor: "#fff8f0",
  },
};

const defaultSettings = {
  enableTheme: true,
  themePreset: "custom", // "rose", "bleu", "vert", "violet", "orange", "custom"
  primaryColor: "#ff69b4",
  lightColor: "#ffc0cb",
  backgroundColor: "#fff5f9",
  borderRadius: 20,
  hideProfilePic: false,
  customTitle: "",
  compactMode: false,
  hideFooter: false,
  hideUserName: false,
  customSchoolName: "",
  enableNoteEditing: false,
  enableHomeworkExport: true,
  // Advanced Features
  enableGradeColoring: true,
  enableQuickCopy: true,
  // Mobile & Accessibility
  mobileOptimized: false,
  largeTouchTargets: false,
  simplifiedLayout: false,
  highContrast: false,
  reducedMotion: false,
  largeText: false,
  // Animations
  animationsEnabled: true,
};

const settings = {};
for (let key in defaultSettings) {
  settings[key] = GM_getValue(key, defaultSettings[key]);
}


// Module: modalUI
function generateModalHTML(settings, predefinedThemes) {
  return `
      <div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;">
          <div style="background: white; padding: 30px; border-radius: 20px; max-width: 600px; max-height: 85vh; overflow-y: auto; box-shadow: 0 10px 40px rgba(0,0,0,0.3);">
              <h2 style="color: ${
                settings.primaryColor
              }; margin-top: 0;">🚀 BetterPronote++ Settings</h2>
              <p style="font-size: 12px; color: #666; margin: 5px 0 15px 0;">Created by Camille Daguin</p>

              <div style="margin: 20px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="enableTheme" ${
                        settings.enableTheme ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Activer le thème rose</span>
                  </label>
              </div>

              <hr style="margin: 20px 0; border: 1px solid #ddd;">
              <h3 style="color: ${settings.primaryColor};">🎨 Couleurs</h3>

              <div style="margin: 15px 0;">
                  <label style="display: block; margin-bottom: 5px;">Couleur principale :</label>
                  <input type="color" id="primaryColor" value="${
                    settings.primaryColor
                  }" style="width: 100%; height: 40px; border-radius: 10px; border: 2px solid #ddd;">
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: block; margin-bottom: 5px;">Couleur claire :</label>
                  <input type="color" id="lightColor" value="${
                    settings.lightColor
                  }" style="width: 100%; height: 40px; border-radius: 10px; border: 2px solid #ddd;">
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: block; margin-bottom: 5px;">Couleur de fond :</label>
                  <input type="color" id="backgroundColor" value="${
                    settings.backgroundColor
                  }" style="width: 100%; height: 40px; border-radius: 10px; border: 2px solid #ddd;">
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: block; margin-bottom: 5px;">Thème prédéfini :</label>
                  <select id="themePreset" style="width: 100%; padding: 10px; border-radius: 10px; border: 2px solid #ddd;">
                      <option value="custom" ${
                        settings.themePreset === "custom" ? "selected" : ""
                      }>Personnalisé</option>
                      <option value="rose" ${
                        settings.themePreset === "rose" ? "selected" : ""
                      }>Rose</option>
                      <option value="bleu" ${
                        settings.themePreset === "bleu" ? "selected" : ""
                      }>Bleu</option>
                      <option value="vert" ${
                        settings.themePreset === "vert" ? "selected" : ""
                      }>Vert</option>
                      <option value="violet" ${
                        settings.themePreset === "violet" ? "selected" : ""
                      }>Violet</option>
                      <option value="orange" ${
                        settings.themePreset === "orange" ? "selected" : ""
                      }>Orange</option>
                  </select>
              </div>



              <hr style="margin: 20px 0; border: 1px solid #ddd;">
              <h3 style="color: ${settings.primaryColor};">✨ Apparence</h3>

              <div style="margin: 15px 0;">
                  <label style="display: block; margin-bottom: 5px;">Arrondi des coins (px) : <span id="radiusValue">${
                    settings.borderRadius
                  }</span></label>
                  <input type="range" id="borderRadius" min="0" max="30" value="${
                    settings.borderRadius
                  }" style="width: 100%;">
              </div>



              <hr style="margin: 20px 0; border: 1px solid #ddd;">
              <h3 style="color: ${
                settings.primaryColor
              };">🎭 Masquer des éléments</h3>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="hideProfilePic" ${
                        settings.hideProfilePic ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Masquer la photo de profil</span>
                  </label>
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="hideUserName" ${
                        settings.hideUserName ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Masquer "Espace Élèves - Nom Prénom"</span>
                  </label>
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="hideFooter" ${
                        settings.hideFooter ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Masquer le footer</span>
                  </label>
              </div>

              <hr style="margin: 20px 0; border: 1px solid #ddd;">
              <h3 style="color: ${
                settings.primaryColor
              };">✏️ Personnalisation</h3>

              <div style="margin: 15px 0;">
                  <label style="display: block; margin-bottom: 5px;">Texte personnalisé en haut :</label>
                  <input type="text" id="customTitle" value="${
                    settings.customTitle
                  }" placeholder="Ex: Bon courage !" style="width: 100%; padding: 10px; border-radius: 10px; border: 2px solid #ddd;">
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: block; margin-bottom: 5px;">Nom du lycée personnalisé :</label>
                  <input type="text" id="customSchoolName" value="${
                    settings.customSchoolName
                  }" placeholder="Laisser vide pour garder l'original" style="width: 100%; padding: 10px; border-radius: 10px; border: 2px solid #ddd;">
              </div>

                    <div style="margin: 15px 0;">
                        <label style="display: flex; align-items: center; margin-bottom: 10px;">
                            <input type="checkbox" id="compactMode" ${
                              settings.compactMode ? "checked" : ""
                            }>
                            <span style="margin-left: 10px;">Mode compact</span>
                        </label>
                    </div>

                    <div style="margin: 15px 0;">
                        <label style="display: flex; align-items: center; margin-bottom: 10px;">
                            <input type="checkbox" id="enableNoteEditing" ${
                              settings.enableNoteEditing ? "checked" : ""
                            }>
                            <span style="margin-left: 10px;">Modifier les notes (visuel seulement)</span>
                        </label>
                    </div>

                    <div style="margin: 15px 0;">
                        <button id="resetNoteModifications" style="padding: 8px 16px; background: #ff6b6b; color: white; border: none; border-radius: 10px; cursor: pointer;">
                            🔄 Réinitialiser les notes modifiées
                        </button>
                    </div>

                    <div style="margin: 15px 0;">
                        <label style="display: flex; align-items: center; margin-bottom: 10px;">
                            <input type="checkbox" id="enableGradeColoring" ${
                              settings.enableGradeColoring ? "checked" : ""
                            }>
                            <span style="margin-left: 10px;">Coloration automatique des notes</span>
                        </label>
                    </div>

                    <div style="margin: 15px 0;">
                        <label style="display: flex; align-items: center; margin-bottom: 10px;">
                            <input type="checkbox" id="enableQuickCopy" ${
                              settings.enableQuickCopy ? "checked" : ""
                            }>
                            <span style="margin-left: 10px;">Copie rapide des notes et devoirs</span>
                        </label>
                    </div>



              <hr style="margin: 20px 0; border: 1px solid #ddd;">
              <h3 style="color: ${
                settings.primaryColor
              };">📱 Mobile & Accessibilité</h3>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="mobileOptimized" ${
                        settings.mobileOptimized ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Optimisation mobile</span>
                  </label>
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="largeTouchTargets" ${
                        settings.largeTouchTargets ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Cibles tactiles larges</span>
                  </label>
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="highContrast" ${
                        settings.highContrast ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Contraste élevé</span>
                  </label>
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="reducedMotion" ${
                        settings.reducedMotion ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Réduire les animations</span>
                  </label>
              </div>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="largeText" ${
                        settings.largeText ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Texte agrandi</span>
                  </label>
              </div>



              <hr style="margin: 20px 0; border: 1px solid #ddd;">
              <h3 style="color: ${settings.primaryColor};">🎨 Animations</h3>

              <div style="margin: 15px 0;">
                  <label style="display: flex; align-items: center; margin-bottom: 10px;">
                      <input type="checkbox" id="animationsEnabled" ${
                        settings.animationsEnabled ? "checked" : ""
                      }>
                      <span style="margin-left: 10px;">Animations activées</span>
                  </label>
              </div>



              <div style="display: flex; gap: 10px; margin-top: 30px;">
                  <button id="saveSettings" style="flex: 1; padding: 12px; background: ${
                    settings.primaryColor
                  }; color: white; border: none; border-radius: 10px; cursor: pointer; font-weight: bold;">
                      💾 Enregistrer
                  </button>
                  <button id="resetSettings" style="flex: 1; padding: 12px; background: #666; color: white; border: none; border-radius: 10px; cursor: pointer;">
                      🔄 Réinitialiser
                  </button>
                  <button id="closeSettings" style="padding: 12px 20px; background: #ddd; color: #333; border: none; border-radius: 10px; cursor: pointer;">
                      ✖️
                  </button>
              </div>
          </div>
      </div>
  `;
}


// Module: modal
function ouvrirParametres() {
  const modal = document.createElement("div");
  modal.id = "pronote-settings-modal";
  modal.innerHTML = generateModalHTML(settings, predefinedThemes);

  document.body.appendChild(modal);

  document.getElementById("borderRadius").addEventListener("input", (e) => {
    document.getElementById("radiusValue").textContent = e.target.value;
  });

  document.getElementById("themePreset").addEventListener("change", (e) => {
    const preset = e.target.value;
    if (preset !== "custom" && predefinedThemes[preset]) {
      document.getElementById("primaryColor").value =
        predefinedThemes[preset].primaryColor;
      document.getElementById("lightColor").value =
        predefinedThemes[preset].lightColor;
      document.getElementById("backgroundColor").value =
        predefinedThemes[preset].backgroundColor;
    }
  });

  document.getElementById("saveSettings").addEventListener("click", () => {
    settings.enableTheme = document.getElementById("enableTheme").checked;
    settings.primaryColor = document.getElementById("primaryColor").value;
    settings.lightColor = document.getElementById("lightColor").value;
    settings.backgroundColor = document.getElementById("backgroundColor").value;
    settings.borderRadius = parseInt(
      document.getElementById("borderRadius").value
    );
    settings.hideProfilePic = document.getElementById("hideProfilePic").checked;
    settings.customTitle = document.getElementById("customTitle").value;
    settings.compactMode = document.getElementById("compactMode").checked;
    settings.hideFooter = document.getElementById("hideFooter").checked;
    settings.hideUserName = document.getElementById("hideUserName").checked;
    settings.customSchoolName =
      document.getElementById("customSchoolName").value;
    settings.enableNoteEditing =
      document.getElementById("enableNoteEditing").checked;
    settings.enableGradeColoring = document.getElementById(
      "enableGradeColoring"
    ).checked;
    settings.enableQuickCopy =
      document.getElementById("enableQuickCopy").checked;

    settings.themePreset = document.getElementById("themePreset").value;
    settings.mobileOptimized =
      document.getElementById("mobileOptimized").checked;
    settings.largeTouchTargets =
      document.getElementById("largeTouchTargets").checked;
    settings.highContrast = document.getElementById("highContrast").checked;
    settings.reducedMotion = document.getElementById("reducedMotion").checked;
    settings.largeText = document.getElementById("largeText").checked;
    settings.animationsEnabled =
      document.getElementById("animationsEnabled").checked;

    for (let key in settings) {
      GM_setValue(key, settings[key]);
    }

    modal.remove();
    location.reload();
  });

  document.getElementById("resetSettings").addEventListener("click", () => {
    if (confirm("Voulez-vous vraiment réinitialiser tous les paramètres ?")) {
      for (let key in defaultSettings) {
        GM_setValue(key, defaultSettings[key]);
      }
      modal.remove();
      location.reload();
    }
  });

  document
    .getElementById("resetNoteModifications")
    .addEventListener("click", () => {
      if (
        confirm(
          "Voulez-vous vraiment réinitialiser toutes les modifications de notes ?"
        )
      ) {
        GM_setValue("noteModifications", {});
        alert("Les modifications de notes ont été réinitialisées.");
        location.reload(); // Recharger pour voir les changements
      }
    });

  document.getElementById("closeSettings").addEventListener("click", () => {
    modal.remove();
  });
}


// Module: styles
function generateColorStyles() {
  return `
    /* Arrière-plan */
    body#id_body,
    .interface_affV {
        background: ${settings.backgroundColor} !important;
        color: #000000 !important;
    }

    /* Header */
    .ObjetBandeauEspace {
        background: ${settings.primaryColor} !important;
    }

    /* Menu */
    .objetBandeauEntete_menu {
        background: ${settings.primaryColor} !important;
    }

    /* Tous les boutons */
    .ieBouton,
    button.small-bt,
    .themeBoutonNeutre {
        background: ${settings.primaryColor} !important;
        color: white !important;
        border-radius: ${settings.borderRadius}px !important;
        border: none !important;
        padding: 8px 20px !important;
    }

    /* Boutons "Tout voir" */
    .widget header .cta-conteneur {
        overflow: visible !important;
    }

    .widget header button {
        white-space: nowrap !important;
        color: white !important;
    }

    .widget header button span {
        color: white !important;
    }

    /* Tags */
    .ie-chips,
    .tag-style {
        background: ${settings.primaryColor} !important;
        color: white !important;
        border-radius: ${settings.borderRadius}px !important;
        padding: 5px 15px !important;
    }

    /* Notes */
    .as-info {
        background: ${settings.primaryColor} !important;
        color: white !important;
        border-radius: ${settings.borderRadius}px !important;
        padding: 8px 15px !important;
    }

    /* Date */
    .date-contain {
        background: ${settings.lightColor} !important;
        color: ${settings.primaryColor} !important;
        padding: 8px 12px !important;
        border-radius: ${settings.borderRadius - 10}px !important;
        font-weight: bold !important;
    }

    /* Détail note */
    .Zone-DetailsNotes header {
        border-bottom: 2px solid ${settings.lightColor} !important;
        padding-bottom: 15px !important;
    }

    .Zone-DetailsNotes .ie-titre {
        color: ${settings.primaryColor} !important;
    }

    .details-notes dt {
        color: ${settings.primaryColor} !important;
        font-weight: bold !important;
    }

    /* Radio buttons */
    .iecb.as-chips {
        border-radius: ${settings.borderRadius}px !important;
        transition: all 0.3s ease !important;
    }

    .iecb.as-chips.is-checked {
        background: ${settings.primaryColor} !important;
        color: white !important;
    }

    .iecb.as-chips:not(.is-checked) {
        background: ${settings.lightColor} !important;
        color: ${settings.primaryColor} !important;
    }

    .iecb.as-chips:hover {
        transform: translateY(-2px) !important;
        box-shadow: 0 3px 10px rgba(0,0,0,0.1) !important;
    }

    /* Zone tri */
    .objetBandeauEntete_thirdmenu {
        background: ${settings.lightColor} !important;
        padding: 10px !important;
    }

    /* Select */
    .ocb_cont.as-select {
        background: white !important;
        border: 2px solid ${settings.lightColor} !important;
        border-radius: ${settings.borderRadius - 5}px !important;
    }

    .ocb_cont.as-select:hover {
      border-color: ${settings.primaryColor} !important;
    }

    /* Dropdown */
    .deroulant-conteneur-show-hide {
        border-radius: ${settings.borderRadius - 5}px !important;
        border: 2px solid ${settings.lightColor} !important;
        box-shadow: 0 5px 20px rgba(0,0,0,0.15) !important;
    }

    .as-li:hover {
        background: ${settings.lightColor} !important;
        color: ${settings.primaryColor} !important;
    }

    .as-li[aria-selected="true"] {
        background: ${settings.primaryColor} !important;
        color: white !important;
    }

    /* Modale */
    .ObjetFenetre_Espace {
        border-radius: ${settings.borderRadius}px !important;
        border: 3px solid ${settings.primaryColor} !important;
    }

    .Fenetre_Titre {
        background: ${settings.primaryColor} !important;
        color: white !important;
        border-radius: ${settings.borderRadius - 5}px ${
    settings.borderRadius - 5
  }px 0 0 !important;
    }
  `;
}

function generateLayoutStyles() {
  return `
    /* Bandeaux (banners) - remove border-radius */
    .objetbandeauentete_global,
    .ObjetBandeauEspace,
    .objetBandeauEntete_menu,
    .objetBandeauEntete_secondmenu,
    .objetBandeauEntete_thirdmenu {
        border-radius: 0 !important;
    }

    /* Buttons inside banners should also be square */
    .objetBandeauEntete_secondmenu .btnImage,
    .objetBandeauEntete_secondmenu .btnImageIcon,
    .objetBandeauEntete_menu .btnImage,
    .objetBandeauEntete_menu .btnImageIcon {
        border-radius: 0 !important;
    }

    /* Widgets */
    .widget,
    section.widget {
        border-radius: ${settings.borderRadius}px !important;
        border: 2px solid ${settings.lightColor} !important;
        overflow: visible !important;
    }

    /* Header des widgets */
    .widget header {
        background: ${settings.lightColor} !important;
        border-radius: ${settings.borderRadius - 2}px ${
    settings.borderRadius - 2
  }px 0 0 !important;
    }

    .widget header h2,
    .widget header h3,
    .widget header span {
        color: ${settings.primaryColor} !important;
    }

    /* Emploi du temps */
    .liste-cours li {
        border-radius: ${settings.borderRadius - 5}px !important;
        margin: 8px 0 !important;
    }

    /* Travail à faire */
    .conteneur-item {
        border-radius: ${settings.borderRadius - 5}px !important;
        margin: 10px 0 !important;
    }

    /* Notes */
    .liste-clickable li {
        border-radius: ${settings.borderRadius - 8}px !important;
        margin: 8px 0 !important;
    }

    /* Inputs */
    input[type="text"],
    input[type="search"],
    .ocb_cont,
    .ocb-libelle {
        border-radius: ${settings.borderRadius - 5}px !important;
        border: 2px solid ${settings.lightColor} !important;
    }

    /* Scrollbar */
    ::-webkit-scrollbar {
        width: 12px !important;
    }

    ::-webkit-scrollbar-thumb {
        background: ${settings.primaryColor} !important;
        border-radius: 10px !important;
    }

    ::-webkit-scrollbar-track {
        background: ${settings.backgroundColor} !important;
        border-radius: 10px !important;
    }

    /* Footer */
    footer.ObjetBandeauPied {
        background: ${settings.primaryColor} !important;
        border-radius: ${settings.borderRadius}px ${
    settings.borderRadius
  }px 0 0 !important;
        ${settings.hideFooter ? "display: none !important;" : ""}
    }

    footer a,
    .ObjetBandeauPied a,
    .ibp-command {
        color: white !important;
    }

    /* Submenu */
    .submenu-wrapper {
        border-radius: ${settings.borderRadius - 5}px !important;
    }

    /* Photo profil */
    .ibe_util_photo {
        ${settings.hideProfilePic ? "display: none !important;" : ""}
    }

    .ibe_util_photo img {
        border-radius: 50% !important;
    }

    /* Wrapper */
    .wrapper-cols {
        padding: ${settings.compactMode ? "5px" : "10px"} !important;
    }

    /* Zone sans événements */
    .no-events {
        border-radius: ${settings.borderRadius - 5}px !important;
        background: ${settings.backgroundColor} !important;
        padding: 20px !important;
    }

    /* Checkbox */
    .iecb span {
        border-radius: 8px !important;
    }

    /* Hover */
    .liste-clickable li:hover,
    .conteneur-item:hover,
    .liste-cours li:hover {
        background: ${settings.backgroundColor} !important;
        transition: background 0.3s ease !important;
    }

    /* Second menu */
    .objetBandeauEntete_secondmenu {
        background: ${settings.lightColor} !important;
    }

    /* Menu items */
    .item-menu_niveau0,
    .item-menu_niveau1,
    .item-menu_niveau2 {
        border-radius: ${settings.borderRadius - 10}px !important;
        margin: 2px 5px !important;
    }

    /* Boutons icône */
    .btnImage,
    .btnImageIcon {
        border-radius: 50% !important;
    }

    /* Lien wrapper */
    .wrapper-link {
        border-radius: ${settings.borderRadius - 8}px !important;
    }

    /* Liste des notes */
    .ListeDernieresNotes,
    .InterfaceDernieresNotes {
        background: white !important;
        border-radius: ${settings.borderRadius}px !important;
        border: 2px solid ${settings.lightColor} !important;
    }

    /* Cellules de notes */
    .liste_celluleGrid {
        border-radius: ${settings.borderRadius - 10}px !important;
        margin: 5px 0 !important;
        transition: all 0.3s ease !important;
    }

    .liste_celluleGrid:hover {
        background: ${settings.backgroundColor} !important;
        transform: translateX(3px) !important;
    }

    .liste_celluleGrid.selected {
        background: ${settings.lightColor} !important;
        border-left: 4px solid ${settings.primaryColor} !important;
    }

    /* Notes affichées */
    .note-devoir {
        background: ${settings.primaryColor} !important;
        color: white !important;
        padding: 5px 15px !important;
        border-radius: ${settings.borderRadius}px !important;
        font-weight: bold !important;
    }

    /* Détail note */
    .Zone-DetailsNotes {
        background: white !important;
        border-radius: ${settings.borderRadius}px !important;
        border: 2px solid ${settings.lightColor} !important;
        padding: 20px !important;
    }
  `;
}

function generateResponsiveStyles() {
  return `
    /* Mobile Optimizations */
    ${
      settings.mobileOptimized
        ? `
    @media (max-width: 768px) {
        .widget {
            margin: 8px 4px !important;
            padding: 12px !important;
        }

        .widget header {
            padding: 12px 8px !important;
            font-size: 14px !important;
        }

        ${
          settings.largeTouchTargets
            ? `
        button, .ieBouton, input, select {
            min-height: 44px !important;
            min-width: 44px !important;
        }
        `
            : ""
        }

        ${
          settings.simplifiedLayout
            ? `
        .submenu-wrapper,
        .objetBandeauEntete_secondmenu {
            display: none !important;
        }

        .widget:not(:has(.important-content)) {
            opacity: 0.6;
        }
        `
            : ""
        }
    }
    `
        : ""
    }

    /* Responsive */
    @media (max-width: 768px) {
        .widget {
            margin: 10px 5px !important;
        }
        .wrapper-cols {
            padding: 5px !important;
        }
    }

    /* Mode compact */
    ${
      settings.compactMode
        ? `
        .widget {
            margin: 5px !important;
        }
        .widget header {
            padding: 8px !important;
        }
        .liste-cours li,
        .conteneur-item,
        .liste-clickable li {
            margin: 4px 0 !important;
            padding: 8px !important;
        }
    `
        : ""
    }
  `;
}

function generateAccessibilityStyles() {
  return `
    /* Accessibility */
    ${
      settings.highContrast
        ? `
    .widget {
      border: 3px solid #000 !important;
    }

    .widget header {
      background: #000 !important;
      color: #fff !important;
    }
    `
        : ""
    }

    ${
      settings.reducedMotion
        ? `
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
    }
    `
        : ""
    }

    ${
      settings.largeText
        ? `
    body, .widget, .widget header h2, .widget header h3 {
      font-size: 1.2em !important;
    }
    `
        : ""
    }

    /* Animation Controls */
    ${
      !settings.animationsEnabled
        ? `
    *, *::before, *::after {
      transition: none !important;
      animation: none !important;
    }
    `
        : `
    .widget:hover {
      transform: translateY(-2px) !important;
      box-shadow: 0 8px 25px rgba(0,0,0,0.15) !important;
      transition: all 0.3s ease !important;
    }
    `
    }
  `;
}

function generateCSS() {
  return `
    ${generateColorStyles()}
    ${generateLayoutStyles()}
    ${generateResponsiveStyles()}
    ${generateAccessibilityStyles()}

    /* Coloration automatique des notes selon la performance */
    ${
      settings.enableGradeColoring
        ? `
    .note-devoir.grade-excellent {
        background: #228B22 !important; /* Vert pour excellent (16-20/20) */
    }

    .note-devoir.grade-good {
        background: #4169E1 !important; /* Bleu/cyan pour bien (12-15/20) */
    }

    .note-devoir.grade-average {
        background: #FF8C00 !important; /* Orange pour moyen (10-11/20) */
    }

    .note-devoir.grade-poor {
        background: #DC143C !important; /* Rouge pour faible (<10/20) */
    }

    .note-devoir.grade-absent {
        background: #808080 !important; /* Gris pour absence */
    }
    `
        : ""
    }
  `;
}


// Module: noteEditing
function enableNoteEditing() {
  const noteModifications = GM_getValue("noteModifications", {});

  function getNoteId(noteElement) {
    // Vérifier si l'élément a déjà un ID attribué
    if (noteElement.hasAttribute("data-note-id")) {
      return noteElement.getAttribute("data-note-id");
    }

    // Essayer de trouver le contexte (matière et date) pour créer un ID plus stable
    let contextId = "";

    // Vérifier si on est dans une popup détail
    const detailContainer = noteElement.closest(".Zone-DetailsNotes");
    if (detailContainer) {
      // Dans la popup détail, chercher le titre et la date
      const titleElement = detailContainer.querySelector(".ie-titre");
      const dateMatch = detailContainer.textContent.match(
        /Note du (\d{1,2} \w+ \d{4})/
      );
      const dateText = dateMatch ? dateMatch[1] : null;

      if (titleElement && dateText) {
        const title = titleElement.textContent.trim();
        contextId = title + "|" + dateText;
      }
    } else {
      // Dans la liste principale
      const noteContainer = noteElement.closest(".liste_celluleGrid");

      if (noteContainer) {
        // Chercher le titre de la matière
        const titleElement = noteContainer.querySelector(".titre-principal");
        const dateElement = noteContainer.querySelector(".date-contain");

        if (titleElement && dateElement) {
          const title = titleElement.textContent.trim();
          const date =
            dateElement.getAttribute("datetime") ||
            dateElement.textContent.trim();
          contextId = title + "|" + date;
        }
      }
    }

    // Si on n'a pas trouvé de contexte, utiliser l'ancienne méthode
    if (!contextId) {
      const originalText = noteElement.textContent.trim();
      const parentElement = noteElement.parentElement;
      const siblingIndex = parentElement
        ? Array.from(parentElement.children).indexOf(noteElement)
        : 0;

      contextId = originalText + "|" + siblingIndex;
    }

    // Utiliser un hash simple du contexte
    let hash = 0;
    for (let i = 0; i < contextId.length; i++) {
      hash = (hash * 31 + contextId.charCodeAt(i)) & 0x7fffffff;
    }

    const noteId = hash.toString(36);
    noteElement.setAttribute("data-note-id", noteId);
    return noteId;
  }

  // Fonction pour restaurer une note modifiée
  function restoreModifiedNote(noteElement) {
    const noteId = getNoteId(noteElement);
    const modifiedValue = noteModifications[noteId];
    if (modifiedValue) {
      const originalText = noteElement.textContent.trim();
      let maxNote = "";
      if (originalText.includes("/")) {
        maxNote = originalText.split("/")[1]?.trim() || "";
      }
      noteElement.innerHTML = "";
      noteElement.textContent = modifiedValue;
      if (maxNote) {
        const slashSpan = document.createElement("span");
        slashSpan.textContent = "/" + maxNote;
        noteElement.appendChild(slashSpan);
      }
    }
  }

  // Fonction pour rendre une note éditable
  function makeNoteEditable(noteElement) {
    const originalText = noteElement.textContent.trim();

    // Extraire la partie note (avant le /)
    let noteValue = originalText;
    let maxNote = "";

    if (originalText.includes("/")) {
      const parts = originalText.split("/");
      noteValue = parts[0].trim();
      maxNote = parts[1] ? parts[1].trim() : "";
    }

    // Créer un input temporaire
    const input = document.createElement("input");
    input.type = "text";
    input.value = noteValue;
    input.style.cssText = `
      background: white;
      border: 2px solid ${settings.primaryColor};
      border-radius: ${settings.borderRadius}px;
      padding: 2px 6px;
      font-size: inherit;
      font-weight: bold;
      color: ${settings.primaryColor};
      width: 60px;
      text-align: center;
    `;

    // Remplacer le contenu par l'input
    noteElement.innerHTML = "";
    noteElement.appendChild(input);
    input.focus();
    input.select();

    // Fonction pour sauvegarder
    function saveNote() {
      const newValue = input.value.trim();
      if (newValue !== "") {
        // Sauvegarder la modification
        const noteId = getNoteId(noteElement);
        noteModifications[noteId] = newValue;
        GM_setValue("noteModifications", noteModifications);

        // Nettoyer le contenu existant et définir la nouvelle valeur
        noteElement.innerHTML = "";
        noteElement.textContent = newValue;
        if (maxNote) {
          const slashSpan = document.createElement("span");
          slashSpan.textContent = "/" + maxNote;
          noteElement.appendChild(slashSpan);
        }
      } else {
        // Restaurer l'original si vide
        noteElement.textContent = originalText;
      }
      noteElement.style.cursor = "pointer";
      noteElement.title = "Cliquez pour modifier";
    }

    // Événements
    input.addEventListener("blur", saveNote);
    input.addEventListener("keydown", (e) => {
      if (e.key === "Enter") {
        saveNote();
      } else if (e.key === "Escape") {
        // Annuler
        noteElement.textContent = originalText;
        noteElement.style.cursor = "pointer";
        noteElement.title = "Cliquez pour modifier";
      }
    });
  }

  // Attendre que les notes soient chargées et ajouter les event listeners
  function addNoteEditingListeners() {
    // Approche simplifiée : rendre tous les éléments de notes directement cliquables
    function makeNoteClickable(noteElement) {
      if (!noteElement.hasAttribute("data-editable")) {
        // Attribuer un ID persistant
        getNoteId(noteElement);

        noteElement.setAttribute("data-editable", "true");
        noteElement.style.cursor = "pointer";
        noteElement.title = "Cliquez pour modifier";

        // Restaurer la note modifiée si elle existe
        restoreModifiedNote(noteElement);

        // Ajouter event listener directement sur l'élément note
        noteElement.addEventListener("click", (e) => {
          e.stopPropagation(); // Empêcher la propagation
          makeNoteEditable(noteElement);
        });
      }
    }

    // Sélectionner toutes les notes
    document.querySelectorAll(".note-devoir").forEach(makeNoteClickable);

    // Sélectionner aussi toutes les notes dans la section détail
    document.querySelectorAll(".details-notes dd").forEach((ddElement) => {
      const text = ddElement.textContent.trim();
      // Rendre modifiables toutes les notes numériques dans la popup détail
      if (
        /^\d+[\.,]\d+$/.test(text) ||
        (/\d/.test(text) && text.includes("/"))
      ) {
        makeNoteClickable(ddElement);
      }
    });
  }

  // Observer les changements dans le DOM pour les nouvelles notes
  const observer = new MutationObserver(() => {
    addNoteEditingListeners();
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });

  // Observer aussi les changements dans les popups détails
  const detailObserver = new MutationObserver(() => {
    // Quand une popup détail apparaît, restaurer les modifications
    setTimeout(() => {
      document.querySelectorAll(".details-notes dt").forEach((dtElement) => {
        if (dtElement.textContent.trim() === "Note élève :") {
          const ddElement = dtElement.nextElementSibling;
          if (ddElement && ddElement.tagName === "DD") {
            if (!ddElement.hasAttribute("data-editable")) {
              ddElement.setAttribute("data-editable", "true");
              ddElement.style.cursor = "pointer";
              ddElement.title = "Cliquez pour modifier";

              // Restaurer la modification si elle existe
              restoreModifiedNote(ddElement);

              ddElement.addEventListener("click", () => {
                makeNoteEditable(ddElement);
              });
            }
          }
        }
      });
    }, 500); // Délai pour s'assurer que le contenu est chargé
  });

  detailObserver.observe(document.body, {
    childList: true,
    subtree: true,
  });

  // Vérifier périodiquement les nouvelles popups détail
  setInterval(() => {
    document.querySelectorAll(".details-notes dd").forEach((ddElement) => {
      const dtElement = ddElement.previousElementSibling;
      if (
        dtElement &&
        dtElement.tagName === "DT" &&
        dtElement.textContent.trim() === "Note élève :"
      ) {
        if (!ddElement.hasAttribute("data-editable")) {
          ddElement.setAttribute("data-editable", "true");
          ddElement.style.cursor = "pointer";
          ddElement.title = "Cliquez pour modifier";
          restoreModifiedNote(ddElement);
          ddElement.addEventListener("click", () =>
            makeNoteEditable(ddElement)
          );
        }
      }
    });
  }, 1000);

  // Ajouter les listeners initiaux
  addNoteEditingListeners();
}


// Module: gradeColoring
function enableGradeColoring() {
  function applyGradeColoring() {
    document.querySelectorAll(".note-devoir").forEach((noteElement) => {
      noteElement.classList.remove(
        "grade-excellent",
        "grade-good",
        "grade-average",
        "grade-poor",
        "grade-absent"
      );

      const text = noteElement.textContent.trim();

      if (text.toLowerCase().includes("abs")) {
        noteElement.classList.add("grade-absent");
        return;
      }

      const gradeMatch = text.match(/^(\d+[\.,]?\d*)(?:[\/\\](\d+))?/);
      if (gradeMatch) {
        let grade = parseFloat(gradeMatch[1].replace(",", "."));
        const scale = gradeMatch[2] ? parseInt(gradeMatch[2]) : 20;

        if (scale === 10) {
          grade = grade * 2;
        }

        const normalizedGrade = Math.round(grade);
        if (normalizedGrade >= 16) {
          noteElement.classList.add("grade-excellent");
        } else if (normalizedGrade >= 12) {
          noteElement.classList.add("grade-good");
        } else if (normalizedGrade >= 10) {
          noteElement.classList.add("grade-average");
        } else {
          noteElement.classList.add("grade-poor");
        }
      }
    });
  }

  applyGradeColoring();

  const observer = new MutationObserver(() => {
    applyGradeColoring();
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });
}


// Module: quickCopy
function enableQuickCopy() {
  function addQuickCopyListeners() {
    document.querySelectorAll(".note-devoir").forEach((noteElement) => {
      if (!noteElement.hasAttribute("data-copy-enabled")) {
        noteElement.setAttribute("data-copy-enabled", "true");
        noteElement.style.cursor = "pointer";
        noteElement.title = "Cliquez pour copier les informations";

        noteElement.addEventListener("click", (e) => {
          if (e.ctrlKey || e.metaKey) {
            return;
          }

          e.preventDefault();
          e.stopPropagation();

          const noteContainer = noteElement.closest(".liste_celluleGrid");
          if (noteContainer) {
            const subjectElement =
              noteContainer.querySelector(".titre-principal");
            const subject = subjectElement
              ? subjectElement.textContent.trim()
              : "Matière inconnue";
            const grade = noteElement.textContent.trim();

            const copyText = `Note : ${grade} | Matière : ${subject}`;
            navigator.clipboard.writeText(copyText).then(() => {
              const originalText = noteElement.textContent;
              noteElement.textContent = "✅ Copié !";
              setTimeout(() => {
                noteElement.textContent = originalText;
              }, 1000);
            });
          }
        });
      }
    });

    document.querySelectorAll(".conteneur-item").forEach((homeworkElement) => {
      if (!homeworkElement.hasAttribute("data-copy-enabled")) {
        homeworkElement.setAttribute("data-copy-enabled", "true");
        homeworkElement.style.cursor = "pointer";
        homeworkElement.title =
          "Cliquez pour copier les informations du devoir";

        homeworkElement.addEventListener("click", (e) => {
          if (e.ctrlKey || e.metaKey) {
            return;
          }

          e.preventDefault();
          e.stopPropagation();

          const subjectElement =
            homeworkElement.querySelector(".titre-matiere");
          const dateElement = homeworkElement.querySelector(".ie-sous-titre");
          const descriptionElement =
            homeworkElement.querySelector(".description");

          const subject = subjectElement
            ? subjectElement.textContent.trim()
            : "Matière inconnue";
          const date = dateElement
            ? dateElement.textContent.trim()
            : "Date inconnue";
          const description = descriptionElement
            ? descriptionElement.textContent.trim()
            : "Description inconnue";

          const copyText = `Date : ${date} | Matière : ${subject} | Travail à faire : ${description}`;
          navigator.clipboard.writeText(copyText).then(() => {
            const originalCursor = homeworkElement.style.cursor;
            homeworkElement.style.cursor = "default";
            homeworkElement.style.backgroundColor = "#d4edda";
            setTimeout(() => {
              homeworkElement.style.cursor = originalCursor;
              homeworkElement.style.backgroundColor = "";
            }, 1000);
          });
        });
      }
    });
  }

  addQuickCopyListeners();

  const observer = new MutationObserver(() => {
    addQuickCopyListeners();
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });
}


// Module: theme
function waitForElement(selector, callback, maxWait = 10000) {
  const startTime = Date.now();
  const check = () => {
    const element = document.querySelector(selector);
    if (element) {
      callback(element);
    } else if (Date.now() - startTime < maxWait) {
      setTimeout(check, 100);
    }
  };
  check();
}

function applyTheme() {
  if (settings.hideUserName) {
    waitForElement(".ibe_util_texte", (userName) => {
      userName.style.display = "none";
    });
  }

  if (settings.customSchoolName && settings.customSchoolName.trim() !== "") {
    waitForElement(".ibe_etab", (schoolName) => {
      const icon = schoolName.querySelector("i");
      const textNode = Array.from(schoolName.childNodes).find(
        (node) => node.nodeType === Node.TEXT_NODE
      );
      if (textNode) {
        textNode.textContent = settings.customSchoolName;
      } else {
        schoolName.textContent = settings.customSchoolName;
        if (icon) schoolName.insertBefore(icon, schoolName.firstChild);
      }
    });
  }

  const style = document.createElement("style");
  style.setAttribute("data-tampermonkey-rose", "true");
  style.textContent = generateCSS();
  document.head.appendChild(style);

  if (settings.customTitle && settings.customTitle.trim() !== "") {
    const customDiv = document.createElement("div");
    customDiv.style.cssText = `
      position: fixed;
      top: 10px;
      right: 10px;
      background: ${settings.primaryColor};
      color: white;
      padding: 10px 20px;
      border-radius: ${settings.borderRadius}px;
      z-index: 9999;
      font-weight: bold;
      box-shadow: 0 4px 15px rgba(0,0,0,0.2);
    `;
    customDiv.textContent = settings.customTitle;
    document.body.appendChild(customDiv);
  }

  if (settings.enableNoteEditing) {
    enableNoteEditing();
  }

  if (settings.enableGradeColoring) {
    enableGradeColoring();
  }

  if (settings.enableQuickCopy) {
    enableQuickCopy();
  }

  console.log("🚀 BetterPronote++ activated! 🚀");
}


// Module: exportHomework
function enableHomeworkExport() {
  function addExportButton() {
    // Look for the homework container
    const homeworkContainer = document.querySelector(".conteneur-liste-CDT");
    if (!homeworkContainer) return;

    // Check if buttons already exist
    if (document.querySelector(".export-buttons-container")) return;

    // Create container for both buttons
    const buttonContainer = document.createElement("div");
    buttonContainer.className = "export-buttons-container";
    buttonContainer.style.cssText =
      "display: flex; gap: 0.5rem; margin-bottom: 1rem; margin-left: 1rem;";

    // TXT Export Button
    const txtButton = document.createElement("button");
    txtButton.className =
      "themeBoutonNeutre small-bt ieBouton ie-ripple NoWrap ieBoutonDefautSansImage";
    txtButton.innerHTML = "📄 TXT";
    txtButton.title = "Exporter en format texte";

    txtButton.addEventListener("click", () => {
      const homeworkData = extractHomeworkData();
      const txtContent = formatHomeworkAsText(homeworkData);
      downloadTextFile(txtContent, "devoirs.txt");
    });

    // ICS Export Button
    const icsButton = document.createElement("button");
    icsButton.className =
      "themeBoutonNeutre small-bt ieBouton ie-ripple NoWrap ieBoutonDefautSansImage";
    icsButton.innerHTML = "📅 ICS";
    icsButton.title = "Exporter vers calendrier (Google Calendar, Outlook...)";

    icsButton.addEventListener("click", () => {
      const homeworkData = extractHomeworkData();
      const icsContent = formatHomeworkAsICS(homeworkData);
      downloadTextFile(icsContent, "devoirs.ics", "text/calendar");
    });

    buttonContainer.appendChild(txtButton);
    buttonContainer.appendChild(icsButton);

    // Insert buttons at the top of the homework list
    const firstElement = homeworkContainer.querySelector(".liste-date");
    if (firstElement && firstElement.parentNode) {
      firstElement.parentNode.insertBefore(buttonContainer, firstElement);
    }
  }

  function extractHomeworkData() {
    const homeworkData = [];

    // Find all date sections
    document.querySelectorAll(".liste-date > li").forEach((dateSection) => {
      const dateHeader = dateSection.querySelector("h2");
      if (!dateHeader) return;

      const dateText = dateHeader.textContent.trim().replace(/^Pour\s+/, "");
      const dateId = dateSection.querySelector("div[id]")?.id || "";

      const assignments = [];

      // Find all assignments for this date
      dateSection.querySelectorAll(".conteneur-item").forEach((item) => {
        const subjectElement = item.querySelector(".titre-matiere");
        const dateInfoElement = item.querySelector(".ie-sous-titre");
        const descriptionElement = item.querySelector(".description");
        const statusElement = item.querySelector(".tag-style");

        const subject = subjectElement ? subjectElement.textContent.trim() : "";
        const dateInfo = dateInfoElement
          ? dateInfoElement.textContent.trim()
          : "";
        const description = descriptionElement
          ? descriptionElement.textContent.trim()
          : "";
        const status = statusElement
          ? statusElement.textContent.trim()
          : "Non spécifié";

        assignments.push({
          subject,
          dateInfo,
          description,
          status,
        });
      });

      if (assignments.length > 0) {
        homeworkData.push({
          date: dateText,
          dateId,
          assignments,
        });
      }
    });

    return homeworkData;
  }

  function formatHomeworkAsText(homeworkData) {
    let text =
      "EXPORT DES DEVOIRS - " + new Date().toLocaleDateString("fr-FR") + "\n";
    text += "=".repeat(50) + "\n\n";

    homeworkData.forEach((dateGroup) => {
      text += `📅 ${dateGroup.date}\n`;
      text += "-".repeat(30) + "\n";

      dateGroup.assignments.forEach((assignment, index) => {
        text += `${index + 1}. ${assignment.subject}\n`;
        text += `   Statut: ${assignment.status}\n`;
        text += `   ${assignment.dateInfo}\n`;
        if (assignment.description) {
          text += `   Description: ${assignment.description.replace(
            /\n/g,
            "\n   "
          )}\n`;
        }
        text += "\n";
      });

      text += "\n";
    });

    text += "=".repeat(50) + "\n";
    text += "Généré par BetterPronote++\n";

    return text;
  }

  function formatHomeworkAsICS(homeworkData) {
    let ics = "BEGIN:VCALENDAR\n";
    ics += "VERSION:2.0\n";
    ics += "PRODID:-//BetterPronote++//Homework Export//FR\n";
    ics += "CALSCALE:GREGORIAN\n";
    ics += "METHOD:PUBLISH\n";

    const now = new Date();
    const timestamp = formatDateForICS(now);

    // Mappage des matières vers des emojis pour plus de visuel
    const subjectEmojis = {
      MATHEMATIQUES: "🔢",
      FRANCAIS: "📖",
      "HISTOIRE-GEOGRAPHIE": "🌍",
      ANGLAIS: "🇬🇧",
      ESPAGNOL: "🇪🇸",
      ALLEMAND: "🇩🇪",
      "PHYSIQUE-CHIMIE": "⚗️",
      "SCIENCES VIE & TERRE": "🧬",
      SES: "💰",
      "ENS. MORAL & CIVIQUE": "⚖️",
      EPS: "⚽",
      "ARTS PLASTIQUES": "🎨",
      MUSIQUE: "🎵",
      TECHNOLOGIE: "🔧",
    };

    homeworkData.forEach((dateGroup) => {
      const dueDate = parseFrenchDate(dateGroup.date);
      if (!dueDate) return; // Skip if date parsing failed

      dateGroup.assignments.forEach((assignment, index) => {
        const eventId = `homework-${
          dateGroup.dateId || dateGroup.date.replace(/\s+/g, "-")
        }-${index}@betterpronote`;

        // Déterminer l'emoji pour la matière
        const emoji = Object.keys(subjectEmojis).find((key) =>
          assignment.subject.toUpperCase().includes(key)
        )
          ? subjectEmojis[
              Object.keys(subjectEmojis).find((key) =>
                assignment.subject.toUpperCase().includes(key)
              )
            ]
          : "📚";

        ics += "BEGIN:VEVENT\n";
        ics += `UID:${eventId}\n`;
        ics += `DTSTART;VALUE=DATE:${formatDateForICS(dueDate, true)}\n`;
        ics += `DTEND;VALUE=DATE:${formatDateForICS(
          new Date(dueDate.getTime() + 24 * 60 * 60 * 1000),
          true
        )}\n`; // End date is next day

        // Titre = le devoir (description) ou matière si pas de description
        const eventTitle =
          assignment.description && assignment.description.trim()
            ? assignment.description.trim().split("\n")[0].substring(0, 100) // Première ligne, max 100 chars
            : assignment.subject;

        ics += `SUMMARY:${escapeICSText(eventTitle)}\n`;

        // Description avec emojis et sans statut
        let description = `📚 Matière: ${assignment.subject}\n`;
        // Extraire seulement la partie date sans [X Jours]
        const dateOnly = assignment.dateInfo.replace(/\s*\[\d+\s*Jours?\]/, "");
        description += `📅 ${dateOnly}`;

        if (assignment.description && assignment.description.trim()) {
          // Ajouter le reste de la description si elle fait plus d'une ligne
          const descriptionLines = assignment.description.trim().split("\n");
          if (descriptionLines.length > 1) {
            description += `\n\n📝 Détails:\n${descriptionLines
              .slice(1)
              .join("\n")}`;
          }
        }

        description += `\n\n🤖 Exporté depuis BetterPronote++`;

        ics += `DESCRIPTION:${escapeICSText(description)}\n`;

        ics += "END:VEVENT\n";
      });
    });

    ics += "END:VCALENDAR\n";
    return ics;
  }

  function calculateUrgency(dateInfo) {
    // Analyse la date pour déterminer l'urgence
    const dayMatch = dateInfo.match(/\[(\d+)\s*Jours?\]/);
    if (dayMatch) {
      const daysLeft = parseInt(dayMatch[1]);
      if (daysLeft <= 1) return 1; // Urgent
      if (daysLeft <= 3) return 5; // Important
      if (daysLeft <= 7) return 7; // Normal
    }
    return 9; // Faible
  }

  function parseFrenchDate(dateString) {
    // Handle various French date formats like "lundi 17 novembre", "mardi 18 novembre", "aujourd'hui", etc.
    const today = new Date();

    // Handle "aujourd'hui"
    if (dateString.toLowerCase().includes("aujourd'hui")) {
      return today;
    }

    // Extract date components - look for patterns like "17 novembre", "18 novembre", etc.
    const dateMatch = dateString.match(/(\d{1,2})\s+(\w+)\s*(\d{4})?/);
    if (dateMatch) {
      const day = parseInt(dateMatch[1]);
      const monthName = dateMatch[2].toLowerCase();
      const year = dateMatch[3] ? parseInt(dateMatch[3]) : today.getFullYear();

      const monthNames = {
        janvier: 0,
        février: 1,
        mars: 2,
        avril: 3,
        mai: 4,
        juin: 5,
        juillet: 6,
        août: 7,
        septembre: 8,
        octobre: 9,
        novembre: 10,
        décembre: 11,
      };

      const month = monthNames[monthName];
      if (month !== undefined) {
        const date = new Date(year, month, day);
        // If the date is in the past and we're in December, assume it's next year
        if (date < today && today.getMonth() === 11 && month < 6) {
          date.setFullYear(year + 1);
        }
        return date;
      }
    }

    // If parsing fails, return null
    return null;
  }

  function formatDateForICS(date, dateOnly = false) {
    if (!date) return "";

    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");

    if (dateOnly) {
      return `${year}${month}${day}`;
    }

    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");

    return `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
  }

  function escapeICSText(text) {
    // Escape special characters for ICS format
    return text
      .replace(/\\/g, "\\\\") // Escape backslashes
      .replace(/;/g, "\\;") // Escape semicolons
      .replace(/,/g, "\\,") // Escape commas
      .replace(/\n/g, "\\n") // Escape newlines
      .replace(/\r/g, "") // Remove carriage returns
      .replace(/:/g, "\\:"); // Escape colons (though not strictly required)
  }

  function downloadTextFile(
    content,
    filename,
    mimeType = "text/plain;charset=utf-8"
  ) {
    const blob = new Blob([content], { type: mimeType });
    const url = URL.createObjectURL(blob);

    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    a.style.display = "none";

    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);

    URL.revokeObjectURL(url);
  }

  // Add button initially
  addExportButton();

  // Watch for DOM changes to add button if homework content loads dynamically
  const observer = new MutationObserver(() => {
    addExportButton();
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });
}


// Module: index
// GM_registerMenuCommand("⚙️ BetterPronote++ Settings", ouvrirParametres);

if (!settings.enableTheme) return;

setTimeout(() => {
  applyTheme();

  // Enable features based on settings
  if (settings.enableHomeworkExport) {
    enableHomeworkExport();
  }
}, 1000);


})();

(function() {
"use strict";

// Bundled modules

// Module: exportHomework
function enableHomeworkExport() {
  function addExportButton() {
    // Look for the homework container
    const homeworkContainer = document.querySelector(".conteneur-liste-CDT");
    if (!homeworkContainer) return;

    // Check if buttons already exist
    if (document.querySelector(".export-buttons-container")) return;

    // Create container for both buttons
    const buttonContainer = document.createElement("div");
    buttonContainer.className = "export-buttons-container";
    buttonContainer.style.cssText =
      "display: flex; gap: 0.5rem; margin-bottom: 1rem; margin-left: 1rem;";

    // TXT Export Button
    const txtButton = document.createElement("button");
    txtButton.className =
      "themeBoutonNeutre small-bt ieBouton ie-ripple NoWrap ieBoutonDefautSansImage";
    txtButton.innerHTML = "📄 TXT";
    txtButton.title = "Exporter en format texte";

    txtButton.addEventListener("click", () => {
      const homeworkData = extractHomeworkData();
      const txtContent = formatHomeworkAsText(homeworkData);
      downloadTextFile(txtContent, "devoirs.txt");
    });

    // ICS Export Button
    const icsButton = document.createElement("button");
    icsButton.className =
      "themeBoutonNeutre small-bt ieBouton ie-ripple NoWrap ieBoutonDefautSansImage";
    icsButton.innerHTML = "📅 ICS";
    icsButton.title = "Exporter vers calendrier (Google Calendar, Outlook...)";

    icsButton.addEventListener("click", () => {
      const homeworkData = extractHomeworkData();
      const icsContent = formatHomeworkAsICS(homeworkData);
      downloadTextFile(icsContent, "devoirs.ics", "text/calendar");
    });

    buttonContainer.appendChild(txtButton);
    buttonContainer.appendChild(icsButton);

    // Insert buttons at the top of the homework list
    const firstElement = homeworkContainer.querySelector(".liste-date");
    if (firstElement && firstElement.parentNode) {
      firstElement.parentNode.insertBefore(buttonContainer, firstElement);
    }
  }

  function extractHomeworkData() {
    const homeworkData = [];

    // Find all date sections
    document.querySelectorAll(".liste-date > li").forEach((dateSection) => {
      const dateHeader = dateSection.querySelector("h2");
      if (!dateHeader) return;

      const dateText = dateHeader.textContent.trim().replace(/^Pour\s+/, "");
      const dateId = dateSection.querySelector("div[id]")?.id || "";

      const assignments = [];

      // Find all assignments for this date
      dateSection.querySelectorAll(".conteneur-item").forEach((item) => {
        const subjectElement = item.querySelector(".titre-matiere");
        const dateInfoElement = item.querySelector(".ie-sous-titre");
        const descriptionElement = item.querySelector(".description");
        const statusElement = item.querySelector(".tag-style");

        const subject = subjectElement ? subjectElement.textContent.trim() : "";
        const dateInfo = dateInfoElement
          ? dateInfoElement.textContent.trim()
          : "";
        const description = descriptionElement
          ? descriptionElement.textContent.trim()
          : "";
        const status = statusElement
          ? statusElement.textContent.trim()
          : "Non spécifié";

        assignments.push({
          subject,
          dateInfo,
          description,
          status,
        });
      });

      if (assignments.length > 0) {
        homeworkData.push({
          date: dateText,
          dateId,
          assignments,
        });
      }
    });

    return homeworkData;
  }

  function formatHomeworkAsText(homeworkData) {
    let text =
      "EXPORT DES DEVOIRS - " + new Date().toLocaleDateString("fr-FR") + "\n";
    text += "=".repeat(50) + "\n\n";

    homeworkData.forEach((dateGroup) => {
      text += `📅 ${dateGroup.date}\n`;
      text += "-".repeat(30) + "\n";

      dateGroup.assignments.forEach((assignment, index) => {
        text += `${index + 1}. ${assignment.subject}\n`;
        text += `   Statut: ${assignment.status}\n`;
        text += `   ${assignment.dateInfo}\n`;
        if (assignment.description) {
          text += `   Description: ${assignment.description.replace(
            /\n/g,
            "\n   "
          )}\n`;
        }
        text += "\n";
      });

      text += "\n";
    });

    text += "=".repeat(50) + "\n";
    text += "Généré par BetterPronote++\n";

    return text;
  }

  function formatHomeworkAsICS(homeworkData) {
    let ics = "BEGIN:VCALENDAR\n";
    ics += "VERSION:2.0\n";
    ics += "PRODID:-//BetterPronote++//Homework Export//FR\n";
    ics += "CALSCALE:GREGORIAN\n";
    ics += "METHOD:PUBLISH\n";

    const now = new Date();
    const timestamp = formatDateForICS(now);

    // Mappage des matières vers des emojis pour plus de visuel
    const subjectEmojis = {
      MATHEMATIQUES: "🔢",
      FRANCAIS: "📖",
      "HISTOIRE-GEOGRAPHIE": "🌍",
      ANGLAIS: "🇬🇧",
      ESPAGNOL: "🇪🇸",
      ALLEMAND: "🇩🇪",
      "PHYSIQUE-CHIMIE": "⚗️",
      "SCIENCES VIE & TERRE": "🧬",
      SES: "💰",
      "ENS. MORAL & CIVIQUE": "⚖️",
      EPS: "⚽",
      "ARTS PLASTIQUES": "🎨",
      MUSIQUE: "🎵",
      TECHNOLOGIE: "🔧",
    };

    homeworkData.forEach((dateGroup) => {
      const dueDate = parseFrenchDate(dateGroup.date);
      if (!dueDate) return; // Skip if date parsing failed

      dateGroup.assignments.forEach((assignment, index) => {
        const eventId = `homework-${
          dateGroup.dateId || dateGroup.date.replace(/\s+/g, "-")
        }-${index}@betterpronote`;

        // Déterminer l'emoji pour la matière
        const emoji = Object.keys(subjectEmojis).find((key) =>
          assignment.subject.toUpperCase().includes(key)
        )
          ? subjectEmojis[
              Object.keys(subjectEmojis).find((key) =>
                assignment.subject.toUpperCase().includes(key)
              )
            ]
          : "📚";

        ics += "BEGIN:VEVENT\n";
        ics += `UID:${eventId}\n`;
        ics += `DTSTART;VALUE=DATE:${formatDateForICS(dueDate, true)}\n`;
        ics += `DTEND;VALUE=DATE:${formatDateForICS(
          new Date(dueDate.getTime() + 24 * 60 * 60 * 1000),
          true
        )}\n`; // End date is next day

        // Titre = le devoir (description) ou matière si pas de description
        const eventTitle =
          assignment.description && assignment.description.trim()
            ? assignment.description.trim().split("\n")[0].substring(0, 100) // Première ligne, max 100 chars
            : assignment.subject;

        ics += `SUMMARY:${escapeICSText(eventTitle)}\n`;

        // Description avec emojis et sans statut
        let description = `📚 Matière: ${assignment.subject}\n`;
        // Extraire seulement la partie date sans [X Jours]
        const dateOnly = assignment.dateInfo.replace(/\s*\[\d+\s*Jours?\]/, "");
        description += `📅 ${dateOnly}`;

        if (assignment.description && assignment.description.trim()) {
          // Ajouter le reste de la description si elle fait plus d'une ligne
          const descriptionLines = assignment.description.trim().split("\n");
          if (descriptionLines.length > 1) {
            description += `\n\n📝 Détails:\n${descriptionLines
              .slice(1)
              .join("\n")}`;
          }
        }

        description += `\n\n🤖 Exporté depuis BetterPronote++`;

        ics += `DESCRIPTION:${escapeICSText(description)}\n`;

        ics += "END:VEVENT\n";
      });
    });

    ics += "END:VCALENDAR\n";
    return ics;
  }

  function calculateUrgency(dateInfo) {
    // Analyse la date pour déterminer l'urgence
    const dayMatch = dateInfo.match(/\[(\d+)\s*Jours?\]/);
    if (dayMatch) {
      const daysLeft = parseInt(dayMatch[1]);
      if (daysLeft <= 1) return 1; // Urgent
      if (daysLeft <= 3) return 5; // Important
      if (daysLeft <= 7) return 7; // Normal
    }
    return 9; // Faible
  }

  function parseFrenchDate(dateString) {
    // Handle various French date formats like "lundi 17 novembre", "mardi 18 novembre", "aujourd'hui", etc.
    const today = new Date();

    // Handle "aujourd'hui"
    if (dateString.toLowerCase().includes("aujourd'hui")) {
      return today;
    }

    // Extract date components - look for patterns like "17 novembre", "18 novembre", etc.
    const dateMatch = dateString.match(/(\d{1,2})\s+(\w+)\s*(\d{4})?/);
    if (dateMatch) {
      const day = parseInt(dateMatch[1]);
      const monthName = dateMatch[2].toLowerCase();
      const year = dateMatch[3] ? parseInt(dateMatch[3]) : today.getFullYear();

      const monthNames = {
        janvier: 0,
        février: 1,
        mars: 2,
        avril: 3,
        mai: 4,
        juin: 5,
        juillet: 6,
        août: 7,
        septembre: 8,
        octobre: 9,
        novembre: 10,
        décembre: 11,
      };

      const month = monthNames[monthName];
      if (month !== undefined) {
        const date = new Date(year, month, day);
        // If the date is in the past and we're in December, assume it's next year
        if (date < today && today.getMonth() === 11 && month < 6) {
          date.setFullYear(year + 1);
        }
        return date;
      }
    }

    // If parsing fails, return null
    return null;
  }

  function formatDateForICS(date, dateOnly = false) {
    if (!date) return "";

    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");

    if (dateOnly) {
      return `${year}${month}${day}`;
    }

    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");

    return `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
  }

  function escapeICSText(text) {
    // Escape special characters for ICS format
    return text
      .replace(/\\/g, "\\\\") // Escape backslashes
      .replace(/;/g, "\\;") // Escape semicolons
      .replace(/,/g, "\\,") // Escape commas
      .replace(/\n/g, "\\n") // Escape newlines
      .replace(/\r/g, "") // Remove carriage returns
      .replace(/:/g, "\\:"); // Escape colons (though not strictly required)
  }

  function downloadTextFile(
    content,
    filename,
    mimeType = "text/plain;charset=utf-8"
  ) {
    const blob = new Blob([content], { type: mimeType });
    const url = URL.createObjectURL(blob);

    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    a.style.display = "none";

    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);

    URL.revokeObjectURL(url);
  }

  // Add button initially
  addExportButton();

  // Watch for DOM changes to add button if homework content loads dynamically
  const observer = new MutationObserver(() => {
    addExportButton();
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });
}


})();