ameliore les chaque fonctionnalite de ce code <!DOCTYPE html>
<html lang="fr">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Editeur Projet Complet</title>
  <style>
    /* === STYLE GÉNÉRAL === */
    body,
    html {
      margin: 0;
      padding: 0;
      height: 100vh;
      font-family: Arial, sans-serif;
      display: flex;
      flex-direction: column;
      background: #282c34;
      color: white;
      user-select: none;
    }

    header {
      background: #21252b;
      padding: 10px;
      display: flex;
      align-items: center;
      gap: 10px;
      user-select: none;
      flex-shrink: 0;
    }

    button,
    select,
    input[type="text"] {
      cursor: pointer;
      background: #3a3f4b;
      border: none;
      border-radius: 4px;
      color: white;
      font-weight: bold;
      padding: 6px 12px;
      font-size: 14px;
      user-select: none;
    }

    button:hover,
    select:hover,
    input[type="text"]:hover {
      background: #5c6070;
    }

    #container {
      flex: 1;
      display: flex;
      height: calc(100vh - 50px);
      overflow: hidden;
      user-select: text;
    }

    #fileExplorer {
      width: 320px;
      background: #21252b;
      overflow-y: auto;
      padding: 10px;
      border-right: 1px solid #444;
      display: flex;
      flex-direction: column;
    }

    #fileExplorerControls,
    #searchGlobalBar {
      display: flex;
      gap: 8px;
      margin-bottom: 10px;
      flex-wrap: wrap;
      align-items: center;
    }

    #searchGlobalBar {
      display: none;
    }

    #editorContainer {
      flex: 1;
      display: flex;
      flex-direction: column;
      height: 100%;
      user-select: text;
      overflow: hidden;
    }

    #tabsBar {
      background: #21252b;
      display: flex;
      gap: 4px;
      padding: 4px;
      overflow-x: auto;
      border-bottom: 1px solid #444;
      flex-shrink: 0;
    }

    .tab {
      padding: 6px 12px;
      background: #333355;
      border-radius: 4px 4px 0 0;
      cursor: pointer;
      user-select: none;
      display: flex;
      align-items: center;
      gap: 6px;
      white-space: nowrap;
    }

    .tab.active {
      background: #555599;
      font-weight: bold;
      color: white;
    }

    .tab .closeBtn {
      font-weight: bold;
      cursor: pointer;
      user-select: none;
    }

    #fileLangSelectContainer {
      padding: 6px 10px;
      background: #1e1e2f;
      user-select: none;
      flex-shrink: 0;
      display: flex;
      align-items: center;
      gap: 10px;
    }

    #fileLangSelect {
      cursor: pointer;
      border-radius: 4px;
      border: none;
      padding: 6px 12px;
      font-weight: bold;
      font-size: 14px;
      background: #3a3f4b;
      color: white;
      user-select: none;
    }

    #editor {
      flex: 1;
      height: 100%;
      outline: none;
      user-select: text;
      position: relative;
    }

    #statusBar {
      height: 28px;
      background: #21252b;
      padding: 0 10px;
      font-family: monospace;
      font-size: 13px;
      line-height: 28px;
      color: #ccc;
      flex-shrink: 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      user-select: none;
      white-space: nowrap;
    }

    #lintMessages {
      height: 120px;
      overflow-y: auto;
      background: #2d2d3e;
      color: #ffa0a0;
      font-family: monospace;
      font-size: 13px;
      padding: 6px 12px;
      user-select: text;
    }

    /* Style arborescence */

    ul,
    li {
      list-style: none;
      padding-left: 12px;
      margin: 2px 0;
    }

    li.folder>span {
      font-weight: bold;
      cursor: pointer;
      user-select: none;
      display: flex;
      align-items: center;
      gap: 5px;
      color: #9cdcfe;
    }

    li.folder.icon::before {
      content: "📁";
    }

    li.file>span {
      cursor: pointer;
      color: #9cdcfe;
      user-select: none;
    }

    li.folder.expanded>ul {
      display: block;
    }

    li.folder>ul {
      display: none;
    }

    li.folder.expanded>span::before {
      content: "📂";
    }

    li[draggable="true"] {
      user-select: none;
      -webkit-user-drag: element;
    }

    /* Drag & Drop highlight */
    .dragover {
      border: 1px dashed #ffcc00;
      background-color: #4a4a50;
    }

    .cm-gutters {
  background-color: #2e2e3e; /* déjà présent */
  color: #888;
  border-right: 1px solid #444;
}

.cm-lineNumbers .cm-gutterElement {
  padding: 0 6px;
}
  </style>
</head>

<body>
  <header>
    <button id="newProjectBtn" title="Créer un nouveau projet">Nouveau Projet</button>
    <button id="saveProjectBtn" title="Sauvegarder tout le projet ZIP">Sauvegarder Projet</button>
    <button id="toggleSearchGlobalBtn" title="Rechercher dans le projet">🔎 Recherche</button>
  </header>

  <div id="container">
    <div id="fileExplorer" aria-label="Explorateur de fichiers et dossiers">
      <div id="fileExplorerControls">
        <select id="parentFolderSelect" style="flex:1;" title="Dossier parent pour création">
          <option value="">(Racine)</option>
        </select>
        <button id="createFileBtn" title="Créer un nouveau fichier">+ Fichier</button>
        <button id="createFolderBtn" title="Créer un nouveau dossier">+ Dossier</button>
      </div>
      <div id="treeRoot" tabindex="0"></div>

      <div id="searchGlobalBar">
        <input type="text" id="searchGlobalInput" placeholder="Recherche globale dans projet..." aria-label="Recherche globale" />
        <button id="searchGlobalClearBtn" title="Effacer recherche">✖</button>
      </div>
    </div>

    <div id="editorContainer" aria-label="Editeur de code">
      <div id="tabsBar" role="tablist" aria-label="Onglets fichiers ouverts"></div>
      <div id="fileLangSelectContainer">
        <label for="fileLangSelect">Langage:</label>
        <select id="fileLangSelect" aria-label="Choisir langage du fichier">
          <option value="plaintext">Texte</option>
          <option value="javascript">JavaScript</option>
          <option value="typescript">TypeScript</option>
          <option value="python">Python</option>
          <option value="html">HTML</option>
          <option value="css">CSS</option>
          <option value="json">JSON</option>
          <option value="markdown">Markdown</option>
          <option value="c">C</option>
          <option value="java">Java</option>
        </select>
      </div>
      <div id="editor" role="main"></div>
      <div id="lintMessages" aria-live="polite" aria-atomic="true"></div>
      <div id="statusBar" role="status" aria-live="polite">Aucun fichier ouvert.</div>
    </div>
  </div>

  <!-- Chargement CodeMirror 6 et Eslint depuis esm.sh et CDN -->
  <script type="module">
    // Imports CodeMirror
    import { EditorState } from "https://esm.sh/@codemirror/state@6.2.0";
    import {
      EditorView,
      keymap,
      highlightActiveLine,
      highlightActiveLineGutter,
    } from "https://esm.sh/@codemirror/view@6.9.3";
    import {
      defaultHighlightStyle,
      syntaxHighlighting,
      indentOnInput,
      bracketMatching,
      foldGutter,
      foldKeymap,
    } from "https://esm.sh/@codemirror/language@6.7.1";
    import { history, historyKeymap } from "https://esm.sh/@codemirror/commands@6.3.0";
    import { closeBrackets, closeBracketsKeymap } from "https://esm.sh/@codemirror/autocomplete@6.6.1";
    import { autocompletion, completionKeymap } from "https://esm.sh/@codemirror/autocomplete@6.6.1";
    import { lintKeymap } from "https://esm.sh/@codemirror/lint@6.2.1";
    import { defaultKeymap } from "https://esm.sh/@codemirror/commands@6.3.0";
    import { HighlightStyle, tags as t } from "https://esm.sh/@codemirror/highlight@6.5.1";

    import { javascript } from "https://esm.sh/@codemirror/lang-javascript@6.1.3";
    import { typescript } from "https://esm.sh/@codemirror/lang-javascript@6.1.3";
    import { python } from "https://esm.sh/@codemirror/lang-python@6.3.0";
    import { html } from "https://esm.sh/@codemirror/lang-html@6.4.0";
    import { css } from "https://esm.sh/@codemirror/lang-css@6.2.0";
    import { json } from "https://esm.sh/@codemirror/lang-json@6.1.2";
    import { markdown, markdownLanguage } from "https://esm.sh/@codemirror/lang-markdown@6.1.4";
    import { cpp } from "https://esm.sh/@codemirror/lang-cpp@6.2.1";
    import { java } from "https://esm.sh/@codemirror/lang-java@6.2.0";
    import { lineNumbers } from "https://esm.sh/@codemirror/gutter@6.3.0";

    // Rechercher globale et surlignage
    import { searchKeymap, highlightSelectionMatches, search } from "https://esm.sh/@codemirror/search@6.5.1";

    // Linter JS simple (ESLint via CDN)
    let eslint = null; // eslint en global après chargement

    // Fonction attendre chargement script (promesse)
    function loadScript(url) {
      return new Promise((res, rej) => {
        const s = document.createElement("script");
        s.src = url;
        s.onload = () => res();
        s.onerror = () => rej(new Error("Erreur chargement script " + url));
        document.head.appendChild(s);
      });
    }

    // Thème sombre personnalisé
    const myTheme = EditorView.theme({
      "&": {
        color: "#d4d4d4",
        backgroundColor: "#1e1e2f",
        height: "100%",
        fontSize: "16px",
      },
      ".cm-content": {
        caretColor: "#ffcc00",
        fontFamily: "'Fira Code', monospace",
      },
      "&.cm-focused .cm-cursor": {
        borderLeftColor: "#ffcc00",
      },
      "&.cm-focused .cm-selectionBackground, ::selection": {
        backgroundColor: "#555581",
      },
      ".cm-gutters": {
        backgroundColor: "#2e2e3e",
        color: "#999",
        border: "none",
      },
      ".cm-activeLine": {
        backgroundColor: "#333355",
      },
      ".cm-foldPlaceholder": {
        backgroundColor: "#444466",
        borderRadius: "3px",
        color: "#ccc",
        padding: "0 3px",
      },
    }, { dark: true });

    const myHighlightStyle = HighlightStyle.define([
      { tag: t.keyword, color: "#c792ea" },
      { tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: "#ffffff" },
      { tag: [t.function(t.variableName), t.labelName], color: "#82aaff" },
      { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: "#ffcb6b" },
      { tag: [t.definition(t.name), t.separator], color: "#ffffff" },
      { tag: [t.typeName, t.className], color: "#ffc777" },
      { tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: "#f78c6c" },
      { tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link], color: "#89ddff" },
      { tag: [t.comment], color: "#626687", fontStyle: "italic" },
      { tag: [t.meta, t.comment], color: "#a5a5a5" },
      { tag: [t.strong], fontWeight: "bold", color: "white" },
      { tag: [t.emphasis], fontStyle: "italic" },
      { tag: [t.strikethrough], textDecoration: "line-through" },
      { tag: [t.link], color: "#89ddff", textDecoration: "underline" },
      { tag: [t.heading], fontWeight: "bold", color: "#ff5370" },
      { tag: [t.atom, t.bool, t.special(t.variableName)], color: "#ff5370" },
      { tag: [t.processingInstruction, t.string, t.inserted], color: "#c3e88d" },
      { tag: [t.invalid], color: "#ff5370" },
    ]);

    // DOM éléments
    const editorDiv = document.getElementById("editor");
    const languageSelect = document.getElementById("fileLangSelect");
    const statusBar = document.getElementById("statusBar");
    const lintMessagesDiv = document.getElementById("lintMessages");
    const newProjectBtn = document.getElementById("newProjectBtn");
    const saveProjectBtn = document.getElementById("saveProjectBtn");
    const createFileBtn = document.getElementById("createFileBtn");
    const createFolderBtn = document.getElementById("createFolderBtn");
    const treeRoot = document.getElementById("treeRoot");
    const parentFolderSelect = document.getElementById("parentFolderSelect");
    const tabsBar = document.getElementById("tabsBar");
    const searchGlobalInput = document.getElementById("searchGlobalInput");
    const searchGlobalBar = document.getElementById("searchGlobalBar");
    const searchGlobalClearBtn = document.getElementById("searchGlobalClearBtn");
    const toggleSearchGlobalBtn = document.getElementById("toggleSearchGlobalBtn");

    // Projet - structure arbre dynamique
    let project = {
      type: "folder",
      name: "MonProjet",
      children: [],
    };

    // Onglets ouverts (tableau d’objets {path, node})
    let openTabs = [];

    // Fichier actif (path)
    let activeFilePath = null;

    // Éditeur CodeMirror
    let editorView = null;

    // Map Language support
    function getLanguageSupport(lang) {
      switch (lang) {
        case "javascript":
          return javascript();
        case "typescript":
          return typescript();
        case "python":
          return python();
        case "html":
          return html();
        case "css":
          return css();
        case "json":
          return json();
        case "markdown":
          return markdown({ base: markdownLanguage });
        case "c":
          return cpp({ dialect: "c" });
        case "java":
          return java();
        default:
          return null;
      }
    }

    // Sauvegarde automatique dans LocalStorage
    function saveProjectToLocalStorage() {
      if (!project) return;
      // Sauvegarde contenu fichier depuis éditeur actif
      if (activeFilePath) {
        const node = findNodeAtPath(activeFilePath);
        if (node) node.content = editorView.state.doc.toString();
      }
      const dataStr = JSON.stringify(project);
      localStorage.setItem("myCodeProject", dataStr);
      console.log("Sauvegarde auto LocalStorage");
    }

    // Chargement projet depuis LocalStorage
    function loadProjectFromLocalStorage() {
      const dataStr = localStorage.getItem("myCodeProject");
      if (!dataStr) return false;
      try {
        project = JSON.parse(dataStr);
        return true;
      } catch {
        return false;
      }
    }

    // Recherche noeud par chemin
    function findNodeAtPath(path) {
      if (!path) return null;
      const parts = path.split("/");
      let node = project;
      if (node.name !== parts[0]) return null;
      for (let i = 1; i < parts.length; i++) {
        if (!node.children) return null;
        node = node.children.find(c => c.name === parts[i]);
        if (!node) return null;
      }
      return node;
    }

    // Construire l’arborescence HTML et ajouter events (drapeaux drag & drop, clic, context menu, sélection)
    function renderTree() {
      treeRoot.innerHTML = "";
      parentFolderSelect.innerHTML = '<option value="">(Racine)</option>';
      function createNodeElem(node, path) {
        const li = document.createElement("li");
        li.dataset.path = path;
        li.draggable = true;

        const span = document.createElement("span");
        span.textContent = node.name;
        li.appendChild(span);

        if (node.type === "folder") {
          li.classList.add("folder");
          li.tabIndex = 0;
          const ul = document.createElement("ul");
          ul.style.marginLeft = "12px";
          for (const c of node.children) {
            ul.appendChild(createNodeElem(c, path + "/" + c.name));
          }
          li.appendChild(ul);
          li.classList.add("collapsed");
          span.addEventListener("click", e => {
            li.classList.toggle("expanded");
            li.classList.toggle("collapsed");
          });
          li.addEventListener("keydown", e => {
            if (e.key === "Enter" || e.key === " ") {
              e.preventDefault();
              li.classList.toggle("expanded");
              li.classList.toggle("collapsed");
            }
          });
          parentFolderSelect.appendChild(new Option(path, path)); // ajouter option dossier courant selection creation
        } else {
          li.classList.add("file");
          li.tabIndex = 0;
          span.addEventListener("click", () => {
            openFile(path);
          });
          li.addEventListener("keydown", e => {
            if (e.key === "Enter" || e.key === " ") {
              e.preventDefault();
              openFile(path);
            }
          });
        }

        // MENU CONTEXTUEL (clic droit)
        li.addEventListener("contextmenu", (e) => {
          e.preventDefault();
          showContextMenu(e.clientX, e.clientY, path, node);
        });

        // Drag & Drop : start
        li.addEventListener("dragstart", e => {
          e.dataTransfer.setData("text/plain", path);
          e.dataTransfer.effectAllowed = "move";
          // Pour le style
          e.currentTarget.style.opacity = "0.5";
        });

        li.addEventListener("dragend", e => {
          e.currentTarget.style.opacity = "";
        });

        // Drag & Drop : dragover / drop (folder uniquement)
        if (node.type === "folder") {
          li.addEventListener("dragover", e => {
            e.preventDefault();
            e.currentTarget.classList.add("dragover");
            e.dataTransfer.dropEffect = "move";
          });
          li.addEventListener("dragleave", e => {
            e.currentTarget.classList.remove("dragover");
          });
          li.addEventListener("drop", e => {
            e.preventDefault();
            e.currentTarget.classList.remove("dragover");
            const srcPath = e.dataTransfer.getData("text/plain");
            moveNode(srcPath, path);
          });
        }
        return li;
      }

      const ulRoot = document.createElement("ul");
      ulRoot.appendChild(createNodeElem(project, project.name));
      treeRoot.appendChild(ulRoot);

      updateParentFolderSelect();
      updateTabs();
    }

    // MOVE NODE dans projet (drag and drop)
    function moveNode(srcPath, destFolderPath) {
      if (srcPath === destFolderPath) {
        alert("Impossible de déplacer un dossier dans lui-même.");
        return;
      }
      const srcNode = findNodeAtPath(srcPath);
      const destNode = findNodeAtPath(destFolderPath);
      if (!srcNode || !destNode || destNode.type !== "folder") return;

      // Vérifier que destFolder n’est pas dans srcNode (éviter boucle)
      if (srcPath === destFolderPath || destFolderPath.startsWith(srcPath + "/")) {
        alert("Impossible de déplacer un dossier dans un de ses descendants.");
        return;
      }

      // Retirer srcNode de son parent
      const srcParentPath = srcPath.split("/").slice(0, -1).join("/");
      const srcParent = findNodeAtPath(srcParentPath);
      if (!srcParent) return;

      const idx = srcParent.children.findIndex(c => c.name === srcNode.name);
      if (idx < 0) return;
      srcParent.children.splice(idx, 1);

      // Ajouter srcNode dans nouveau dossier
      if (!destNode.children) destNode.children = [];

      // Vérifier doublon nom
      if (destNode.children.find(c => c.name === srcNode.name)) {
        alert(`Un élément avec le nom "${srcNode.name}" existe déjà dans "${destNode.name}".`);
        // Remettre à l'ancien emplacement si tu veux, mais on annule juste move ici
        srcParent.children.splice(idx, 0, srcNode);
        return;
      }

      destNode.children.push(srcNode);

      // Si fichier ouvert sous srcPath, mettre à jour path
      if (openTabs.find(t => t.path === srcPath)) {
        const tab = openTabs.find(t => t.path === srcPath);
        tab.path = destFolderPath + "/" + srcNode.name;
        if (activeFilePath === srcPath) activeFilePath = tab.path;
      }

      renderTree();
      saveProjectToLocalStorage();
    }

    // Context menu custom minimaliste (désactive clic par défaut)
    let contextMenuElem = null;
    function showContextMenu(x, y, path, node) {
      if (contextMenuElem) {
        contextMenuElem.remove();
        contextMenuElem = null;
      }
      contextMenuElem = document.createElement("div");
      contextMenuElem.style.position = "fixed";
      contextMenuElem.style.left = x + "px";
      contextMenuElem.style.top = y + "px";
      contextMenuElem.style.background = "#3a3f4b";
      contextMenuElem.style.borderRadius = "4px";
      contextMenuElem.style.boxShadow = "0 0 10px #0008";
      contextMenuElem.style.zIndex = 1000;
      contextMenuElem.style.padding = "4px 0";
      contextMenuElem.style.minWidth = "130px";
      contextMenuElem.tabIndex = 0;

      // Actions
      function createMenuItem(text, onClick) {
        const div = document.createElement("div");
        div.textContent = text;
        div.style.padding = "6px 12px";
        div.style.cursor = "pointer";
        div.addEventListener("mouseenter", () => div.style.background = "#555599");
        div.addEventListener("mouseleave", () => div.style.background = "");
        div.addEventListener("click", () => {
          onClick();
          closeContextMenu();
        });
        return div;
      }

      const renameItem = createMenuItem("Renommer", () => {
        let newName = prompt("Nouveau nom :", node.name);
        if (!newName || newName.trim() === "" || newName === node.name) return;
        if (!isValidName(newName)) { alert("Nom invalide."); return; }

        const parentPath = path.split("/").slice(0, -1).join("/");
        const parentNode = findNodeAtPath(parentPath);
        if (!parentNode) return;

        if (parentNode.children.some(c => c.name === newName)) {
          alert("Un élément avec ce nom existe déjà ici !");
          return;
        }

        node.name = newName;

        // Mise à jour paths onglets ouverts
        for (let tab of openTabs) {
          if (tab.path.startsWith(path)) {
            tab.path = newName + tab.path.slice(path.length);
            if (activeFilePath === path) activeFilePath = tab.path;
          }
        }

        renderTree();
        updateTabs();
        saveProjectToLocalStorage();
      });

      const deleteItem = createMenuItem("Supprimer", () => {
        if (!confirm(`Supprimer "${node.name}" ? Cette action est irréversible.`)) return;

        const parentPath = path.split("/").slice(0, -1).join("/");
        const parentNode = findNodeAtPath(parentPath);
        if (!parentNode) return;

        // Remove from parent's children
        const idx = parentNode.children.findIndex(c => c === node);
        if (idx !== -1) {
          parentNode.children.splice(idx, 1);
        }

        // Ferme onglets ouverts sur cet item ou sous-arborescence
        openTabs = openTabs.filter(tab => !tab.path.startsWith(path));
        if (activeFilePath && activeFilePath.startsWith(path)) activeFilePath = null;

        renderTree();
        updateTabs();
        clearEditor();
        saveProjectToLocalStorage();
      });

      contextMenuElem.appendChild(renameItem);
      contextMenuElem.appendChild(deleteItem);

      document.body.appendChild(contextMenuElem);

      // Fermer au clic en dehors
      setTimeout(() => {
        window.addEventListener("click", closeContextMenu, { once: true });
        window.addEventListener("contextmenu", closeContextMenu, { once: true });
      }, 0);
    }

    function closeContextMenu() {
      if (contextMenuElem) {
        contextMenuElem.remove();
        contextMenuElem = null;
      }
    }

    // Validation simple nom fichiers/dossiers
    function isValidName(name) {
      // Simple : pas de / ni caractères spéciaux
      return /^[^\\/:\*\?"<>\|]+$/.test(name.trim());
    }

    // Mise à jour select des dossiers parents pour création
    function updateParentFolderSelect() {
      parentFolderSelect.innerHTML = '<option value="">(Racine)</option>';
      function addPaths(node, path) {
        if (node.type === "folder") {
          parentFolderSelect.appendChild(new Option(path, path));
          if (node.children) {
            for (const c of node.children) {
              addPaths(c, path + "/" + c.name);
            }
          }
        }
      }
      addPaths(project, project.name);
    }

    // Création fichier/dossier avec dossier parent sélectionné
    createFileBtn.addEventListener("click", () => {
      const filename = prompt("Nom fichier (avec extension) ?");
      if (!filename) return alert("Nom invalide");
      if (!isValidName(filename)) return alert("Nom invalide");

      let parentPath = parentFolderSelect.value;
      if (!parentPath) parentPath = project.name;

      const parentNode = findNodeAtPath(parentPath);
      if (!parentNode || parentNode.type !== "folder") return alert("Dossier parent invalide");

      if (parentNode.children.some(c => c.name === filename)) {
        return alert("Un élément avec ce nom existe déjà dans ce dossier");
      }

      const newFile = {
        type: "file",
        name: filename,
        language: detectLanguageFromFilename(filename),
        content: "",
      };
      parentNode.children.push(newFile);
      renderTree();
      saveProjectToLocalStorage();
    });

    createFolderBtn.addEventListener("click", () => {
      const folderName = prompt("Nom dossier ?");
      if (!folderName) return alert("Nom invalide");
      if (!isValidName(folderName)) return alert("Nom invalide");

      let parentPath = parentFolderSelect.value;
      if (!parentPath) parentPath = project.name;

      const parentNode = findNodeAtPath(parentPath);
      if (!parentNode || parentNode.type !== "folder") return alert("Dossier parent invalide");

      if (parentNode.children.some(c => c.name === folderName)) {
        return alert("Un élément avec ce nom existe déjà dans ce dossier");
      }

      const newFolder = {
        type: "folder",
        name: folderName,
        children: [],
      };
      parentNode.children.push(newFolder);
      renderTree();
      saveProjectToLocalStorage();
    });

    // Détecter langage via extension nom fichier
    function detectLanguageFromFilename(name) {
      if (!name) return "plaintext";
      const ext = name.split(".").pop().toLowerCase();
      switch (ext) {
        case "js": return "javascript";
        case "ts": return "typescript";
        case "py": return "python";
        case "html": case "htm": return "html";
        case "css": return "css";
        case "json": return "json";
        case "md": return "markdown";
        case "c": return "c";
        case "java": return "java";
        default: return "plaintext";
      }
    }

    // === Onglets ===

    function updateTabs() {
      tabsBar.innerHTML = "";
      openTabs.forEach(tab => {
        const btn = document.createElement("div");
        btn.className = "tab";
        if (tab.path === activeFilePath) btn.classList.add("active");
        btn.title = tab.path;
        btn.textContent = tab.node.name;

        // Bouton fermé
        const closeBtn = document.createElement("span");
        closeBtn.className = "closeBtn";
        closeBtn.textContent = "×";
        closeBtn.title = "Fermer cet onglet";
        closeBtn.addEventListener("click", (e) => {
          e.stopPropagation();
          closeTab(tab.path);
        });
        btn.appendChild(closeBtn);

        btn.addEventListener("click", () => {
          if (tab.path !== activeFilePath) switchToTab(tab.path);
        });
        tabsBar.appendChild(btn);
      });
    }

    function switchToTab(path) {
      if (!openTabs.find(t => t.path === path)) return;
      activeFilePath = path;
      openFile(path);
      updateTabs();
    }

    function closeTab(path) {
      const idx = openTabs.findIndex(t => t.path === path);
      if (idx === -1) return;
      openTabs.splice(idx, 1);
      if (activeFilePath === path) {
        if (openTabs.length) {
          const nextTab = openTabs[Math.max(0, idx - 1)];
          activeFilePath = nextTab.path;
          openFile(activeFilePath);
        } else {
          activeFilePath = null;
          clearEditor();
        }
      }
      updateTabs();
    }

    // Ouvrir fichier, ajouter à onglets
    function openFile(path) {
      const node = findNodeAtPath(path);
      if (!node || node.type !== "file") return alert("Fichier introuvable");
      // Sauvegarder contenu précédent éditeur
      if (activeFilePath) {
        const prevNode = findNodeAtPath(activeFilePath);
        if (prevNode) prevNode.content = editorView.state.doc.toString();
      }

      activeFilePath = path;
      if (!openTabs.find(t => t.path === path)) {
        openTabs.push({ path, node });
      }

      // Langage
      languageSelect.disabled = false;
      languageSelect.value = node.language || detectLanguageFromFilename(node.name);
      updateTabs();

      // Config éditeur avec langage et contenu
      loadEditorContent(node.content || "", languageSelect.value);

      statusBar.textContent = `Édition: ${path}`;
      clearLintMessages();

      // Focaliser éditeur
      editorView.focus();
    }

    // Nettoyer l’éditeur (aucun fichier ouvert)
    function clearEditor() {
      if (activeFilePath) {
        const node = findNodeAtPath(activeFilePath);
        if (node) node.content = editorView.state.doc.toString();
      }
      activeFilePath = null;
      languageSelect.disabled = false;
      updateTabs();
      loadEditorContent("", null);
      statusBar.textContent = "Aucun fichier ouvert.";
      clearLintMessages();
    }

    // Charger contenu dans l’éditeur avec langage choisi
    function loadEditorContent(content, lang) {
      const langSupport = getLanguageSupport(lang);
      if (editorView) editorView.destroy();
      editorView = new EditorView({
        state: EditorState.create({
          doc: content || "",
          extensions: [
            lineNumbers(),
            history(),
            closeBrackets(),
            autocompletion(),
            indentOnInput(),
            bracketMatching(),
            foldGutter(),
            syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
            syntaxHighlighting(myHighlightStyle),
            highlightActiveLine(),
            highlightActiveLineGutter(),
            keymap.of([
              ...defaultKeymap,
              ...closeBracketsKeymap,
              ...historyKeymap,
              ...foldKeymap,
              ...completionKeymap,
              ...lintKeymap,
              ...searchKeymap,
            ]),
            langSupport ? langSupport : [],
            myTheme,
            highlightSelectionMatches(),
            search({ top: true }),
          ],
        }),
        parent: editorDiv,
      });

      // Listener pour save automatique + mise à jour statut + linter
      editorView.dispatch({
        effects: EditorView.updateListener.of(update => {
          if (update.docChanged) {
            onEditorContentChanged();
          }
          if (update.selectionSet) updateStatusBar();
        }),
      });

      updateStatusBar();
      runLint();
    }

    // Mise à jour barre statut ligne/colonne
    function updateStatusBar() {
      if (!editorView) return;
      const pos = editorView.state.selection.main.head;
      const line = editorView.state.doc.lineAt(pos);
      const col = pos - line.from + 1;
      const lineNumber = line.number;
      statusBar.textContent = `Édition: ${activeFilePath || "[Aucun fichier]"} — Ligne ${lineNumber}, Colonne ${col}`;
    }

    // Quand utilisateur modifie l’éditeur
    function onEditorContentChanged() {
      if (!activeFilePath) return;
      const node = findNodeAtPath(activeFilePath);
      if (!node) return;
      node.content = editorView.state.doc.toString();
      saveProjectToLocalStorage();
      updateStatusBar();
      runLint();
    }

    // Changement langage fichier
    languageSelect.addEventListener("change", () => {
      if (!activeFilePath) return;
      const node = findNodeAtPath(activeFilePath);
      if (!node) return;
      node.language = languageSelect.value;
      // Recharge édition pour changer coloration
      loadEditorContent(node.content, node.language);
      saveProjectToLocalStorage();
    });

    // Nouveau projet
    newProjectBtn.addEventListener("click", () => {
      if (!confirm("Créer un nouveau projet ? Tout travail non sauvegardé sera perdu.")) return;
      project = {
        type: "folder",
        name: "MonProjet",
        children: [],
      };
      openTabs = [];
      activeFilePath = null;
      languageSelect.disabled = false;
      clearEditor();
      renderTree();
      saveProjectToLocalStorage();
    });

    // Sauvegarder projet zip
    saveProjectBtn.addEventListener("click", async () => {
      if (!project) return alert("Projet vide");
      if (activeFilePath) onEditorContentChanged();
      const zipModule = await import("https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.0/jszip.min.js");
      const JSZip = zipModule.default;
      const zip = new JSZip();

      function addFolder(zipFolder, node) {
        node.children.forEach(child => {
          if (child.type === "folder") {
            addFolder(zipFolder.folder(child.name), child);
          } else if (child.type === "file") {
            zipFolder.file(child.name, child.content || "");
          }
        });
      }

      const rootFolder = zip.folder(project.name);
      addFolder(rootFolder, project);

      const content = await zip.generateAsync({ type: "blob" });

      const a = document.createElement("a");
      a.href = URL.createObjectURL(content);
      a.download = `${project.name}.zip`;
      document.body.appendChild(a);
      a.click();
      setTimeout(() => {
        document.body.removeChild(a);
        URL.revokeObjectURL(a.href);
      }, 0);
    });

    // === Recherche globale projet ===

    toggleSearchGlobalBtn.addEventListener("click", () => {
      if (searchGlobalBar.style.display === "flex") {
        hideSearchGlobal();
      } else {
        showSearchGlobal();
      }
    });

    function showSearchGlobal() {
      searchGlobalBar.style.display = "flex";
      searchGlobalInput.focus();
      searchGlobalInput.select();
      refreshSearchGlobalResults();
    }

    function hideSearchGlobal() {
      searchGlobalBar.style.display = "none";
      clearSearchGlobalResults();
    }

    searchGlobalClearBtn.addEventListener("click", () => {
      searchGlobalInput.value = "";
      refreshSearchGlobalResults();
      searchGlobalInput.focus();
    });

    searchGlobalInput.addEventListener("input", () => {
      refreshSearchGlobalResults();
    });

    const globalSearchResultsDiv = document.createElement("div");
    globalSearchResultsDiv.style.overflowY = "auto";
    globalSearchResultsDiv.style.flex = "1";
    globalSearchResultsDiv.style.marginTop = "4px";
    globalSearchResultsDiv.style.paddingTop = "4px";
    globalSearchResultsDiv.style.maxHeight = "300px";
    searchGlobalBar.appendChild(globalSearchResultsDiv);

    function refreshSearchGlobalResults() {
      globalSearchResultsDiv.innerHTML = "";
      const query = searchGlobalInput.value.trim().toLowerCase();
      if (!query) return;

      const results = [];
      function searchInNode(node, path) {
        if (node.type === "file") {
          if (node.content && node.content.toLowerCase().includes(query)) {
            results.push({ path, node });
          }
        } else if (node.type === "folder" && node.children) {
          for (const c of node.children) {
            searchInNode(c, path + "/" + c.name);
          }
        }
      }
      searchInNode(project, project.name);

      if (results.length === 0) {
        globalSearchResultsDiv.textContent = "Aucun résultat";
        return;
      }

      results.forEach(({ path, node }) => {
        const div = document.createElement("div");
        div.style.padding = "4px 6px";
        div.style.cursor = "pointer";
        div.textContent = path;
        div.title = "Ouvrir " + path;
        div.addEventListener("click", () => {
          openFile(path);
          hideSearchGlobal();
        });
        div.addEventListener("mouseenter", () => div.style.backgroundColor = "#4a4a60");
        div.addEventListener("mouseleave", () => div.style.backgroundColor = "");
        globalSearchResultsDiv.appendChild(div);
      });
    }

    function clearSearchGlobalResults() {
      globalSearchResultsDiv.innerHTML = "";
    }

    // === Linter JS basique avec eslint-web ===
    async function runLint() {
      if (!activeFilePath) {
        lintMessagesDiv.textContent = "";
        return;
      }
      const node = findNodeAtPath(activeFilePath);
      if (!node || (node.language !== "javascript" && node.language !== "typescript")) {
        lintMessagesDiv.textContent = "";
        return;
      }
      try {
        if (!eslint) {
          await loadScript("https://cdn.jsdelivr.net/npm/eslint@8.38.0/dist/eslint.min.js");
          eslint = window.ESLint;
        }
        // eslint d'un coté pas simple à utiliser directement ici,
        // pour simplifier on utilisera eslint.linter API basique (faux lint)
        const linter = new eslint.Linter();
        const messages = linter.verify(node.content || "", {
          env: { browser: true, es6: true },
          parserOptions: { ecmaVersion: 2021, sourceType: "module" },
          rules: { semi: "error", "no-unused-vars": "warn", "no-undef": "error" },
        });

        if (!messages.length) {
          lintMessagesDiv.textContent = "Aucune erreur détectée";
          return;
        }
        lintMessagesDiv.innerHTML = "";
        for (const msg of messages) {
          const el = document.createElement("div");
          el.textContent = `[${msg.line}:${msg.column}] ${msg.message} (${msg.ruleId}) - ${msg.severity === 2 ? "Erreur" : "Avertissement"}`;
          lintMessagesDiv.appendChild(el);
        }
      } catch (err) {
        lintMessagesDiv.textContent = "Erreur lint: " + err.message;
      }
    }

    // === Initialisation ===

    // Charger projet sauvegardé
    if (!loadProjectFromLocalStorage()) {
      newProjectBtn.click();
    }
    renderTree();

    // Prise en compte openTabs sauvegardés si souhaité (ici on démarre vide)
    openTabs = [];
    activeFilePath = null;
    clearEditor();

    // Sauvegarde auto toutes 10sec (en plus save à chaque modif)
    setInterval(() => {
      saveProjectToLocalStorage();
    }, 10000);
  </script>
</body>

</html>