-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathtask-manager.html
More file actions
1 lines (1 loc) · 111 KB
/
Copy pathtask-manager.html
File metadata and controls
1 lines (1 loc) · 111 KB
1
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Markdown Task Manager</title><style>*{margin:0;padding:0;box-sizing:border-box}:root{--bg-primary:#f5f5f5;--bg-secondary:#ffffff;--bg-tertiary:#f8f9fa;--bg-input:#ffffff;--border-color:#e0e0e0;--border-input:#cbd5e0;--text-primary:#212121;--text-secondary:#757575;--text-label:#333333;--accent:#2196F3;--accent-hover:#1976D2;--shadow:0 2px 4px rgba(0,0,0,0.1);--shadow-hover:0 4px 8px rgba(0,0,0,0.15);--badge-category-bg:#E3F2FD;--badge-category-text:#1565C0;--badge-assignee-bg:#F3E5F5;--badge-assignee-text:#6A1B9A;--tag-bg:#FFF3E0;--tag-text:#E65100;--notification-bg:#ffffff;--debug-bg:#f9f9f9;--debug-border:#ddd;--btn-secondary-hover:#e8e8e8;--search-clear-color:#718096;--bg:var(--bg-secondary);--text:var(--text-primary);--border:var(--border-color);--priority-default:#888888;--priority-red:#EF4444;--priority-orange:#F97316;--priority-yellow:#EAB308;--priority-green:#22C55E;--priority-blue:#3B82F6;--priority-purple:#A855F7;--priority-white:#E5E7EB;--priority-black:#1F2937}[data-theme=dark]{--bg-primary:#1a1a2e;--bg-secondary:#16213e;--bg-tertiary:#1a1a2e;--bg-input:#0f3460;--border-color:#2a2a4a;--border-input:#3a3a5a;--text-primary:#e0e0e0;--text-secondary:#a0a0b8;--text-label:#c8c8d8;--accent:#4facfe;--accent-hover:#3a8fd4;--shadow:0 2px 4px rgba(0,0,0,0.3);--shadow-hover:0 4px 8px rgba(0,0,0,0.4);--badge-category-bg:#1a3a5c;--badge-category-text:#64b5f6;--badge-assignee-bg:#2a1a3c;--badge-assignee-text:#ce93d8;--tag-bg:#3c2a1a;--tag-text:#ffb74d;--notification-bg:#16213e;--debug-bg:#1a1a2e;--debug-border:#2a2a4a;--btn-secondary-hover:#2a2a4a;--search-clear-color:#a0a0b8}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;background:var(--bg-primary);color:var(--text-primary);line-height:1.6}.header{background:var(--bg-secondary);border-bottom:1px solid var(--border-color);padding:1rem 2rem;box-shadow:var(--shadow);position:sticky;top:0;z-index:100}.header-content{max-width:1400px;margin:0 auto;display:flex;justify-content:space-between;align-items:center}h1{font-size:1.5rem;font-weight:600;color:var(--text-primary)}.btn{padding:.6rem 1.2rem;border:none;border-radius:6px;font-size:.9rem;font-weight:500;cursor:pointer;transition:all .2s;display:inline-flex;align-items:center;gap:.5rem}.btn-primary{background:var(--accent);color:#fff}.btn-primary:hover{background:var(--accent-hover);box-shadow:var(--shadow-hover)}.btn-secondary{background:var(--bg-primary);color:var(--text-primary);border:1px solid var(--border-color)}.btn-secondary:hover{background:var(--btn-secondary-hover)}.container{max-width:1400px;margin:2rem auto;padding:0 2rem}.welcome{text-align:center;padding:4rem 2rem}.welcome h2{font-size:2rem;margin-bottom:1rem;color:var(--text-primary)}.welcome p{color:var(--text-secondary);margin-bottom:2rem;font-size:1.1rem}#kanbanView{margin:0;padding:0}.kanban-board{display:flex;gap:1.5rem;margin:0;padding:2rem;overflow-x:auto;justify-content:flex-start}.kanban-column{background:var(--bg-secondary);border-radius:8px;padding:1rem;box-shadow:var(--shadow);min-width:280px;flex:1 1 0;display:flex;flex-direction:column}.column-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;padding-bottom:.75rem;border-bottom:2px solid var(--border-color)}.column-title{font-size:1.1rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.column-count{background:var(--bg-primary);padding:.25rem .6rem;border-radius:12px;font-size:.85rem;color:var(--text-secondary)}.task-list{min-height:100px;flex:1;display:flex;flex-direction:column;gap:.75rem}.task-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:6px;padding:1rem;cursor:move;transition:all .2s}.task-card:hover{box-shadow:var(--shadow-hover);transform:translateY(-2px)}.task-card.dragging{opacity:.5}.column-add-btn{background:var(--accent);color:#fff;border:none;border-radius:6px;width:28px;height:28px;font-size:1.2rem;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .2s}.column-add-btn:hover{background:var(--accent-hover)}.kanban-column.drop-target{outline:2px dashed var(--accent);outline-offset:-4px}.task-header{display:flex;justify-content:space-between;align-items:start;margin-bottom:.5rem}.task-id{font-size:.75rem;color:var(--text-secondary);font-weight:500}.task-title{font-weight:600;font-size:.95rem;margin-bottom:.5rem;color:var(--text-primary)}.task-meta{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.75rem}.badge{padding:.25rem .6rem;border-radius:4px;font-size:.75rem;font-weight:500}.badge-priority{color:#fff;background:var(--priority-default)}.badge-priority.Default{background:var(--priority-default)}.badge-priority.Red{background:var(--priority-red)}.badge-priority.Orange{background:var(--priority-orange)}.badge-priority.Yellow{background:var(--priority-yellow)}.badge-priority.Green{background:var(--priority-green)}.badge-priority.Blue{background:var(--priority-blue)}.badge-priority.Purple{background:var(--priority-purple)}.badge-priority.White{background:var(--priority-white);color:#1f2937}.badge-priority.Black{background:var(--priority-black)}.badge-category{background:var(--badge-category-bg);color:var(--badge-category-text)}.badge-assignee{background:var(--badge-assignee-bg);color:var(--badge-assignee-text)}.tag{background:var(--tag-bg);color:var(--tag-text);padding:.2rem .5rem;border-radius:3px;font-size:.7rem}.badge-due{background:var(--bg-tertiary);color:var(--text-secondary)}.badge-due--soon{background:#f59e0b;color:#fff}.badge-due--overdue{background:#ef4444;color:#fff}.task-card--soon{border-left:4px solid #f59e0b}.task-card--overdue{border-left:4px solid #ef4444}.task-description{font-size:.85rem;color:var(--text-secondary);margin-top:.5rem;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.task-subtasks{margin-top:.75rem;font-size:.8rem;color:var(--text-secondary)}.subtask-progress{display:flex;align-items:center;gap:.5rem}.progress-bar{flex:1;height:4px;background:var(--bg-primary);border-radius:2px;overflow:hidden}.progress-fill{height:100%;background:var(--accent);transition:width .3s}pre{margin:1rem 0;border-radius:6px;overflow-x:auto;background:#2d2d2d;padding:1rem}pre code{font-family:Consolas,Monaco,'Courier New',monospace;font-size:.9em;line-height:1.5;color:#f8f8f2;display:block}code{background:#2d2d2d;color:#f8f8f2;padding:.125rem .35rem;border-radius:3px;font-family:Consolas,Monaco,monospace;font-size:.9em}pre code{background:0 0;padding:0}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:1000;align-items:center;justify-content:center}.modal.active{display:flex}.modal-content{background:var(--bg-secondary);border-radius:8px;padding:2rem;width:80%;max-height:90vh;overflow-y:auto}.modal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1.5rem}.modal-header h2{font-size:1.5rem}.close-btn{background:0 0;border:none;font-size:1.5rem;cursor:pointer;color:var(--text-secondary)}.form-group{margin-bottom:1.5rem}.form-group label{display:block;margin-bottom:.5rem;font-weight:500;color:var(--text-primary)}.form-group input,.form-group select,.form-group textarea{width:100%;padding:.75rem;border:1px solid var(--border-color);border-radius:6px;font-size:.95rem;font-family:inherit}.form-group textarea{min-height:100px;resize:vertical}.task-detail{line-height:1.8}.task-detail strong{color:var(--text-primary)}.actions{display:flex;gap:1rem;margin-top:1.5rem}.notification{position:fixed;bottom:2rem;right:2rem;background:var(--notification-bg);padding:1rem 1.5rem;border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);display:none;align-items:center;gap:.75rem;z-index:1001}.notification.show{display:flex;animation:slideIn .3s ease}@keyframes slideIn{from{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}.notification.success{border-left:4px solid #22c55e}.notification.error{border-left:4px solid #ef4444}.loading{display:inline-block;width:20px;height:20px;border:3px solid var(--bg-primary);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}#deleteProjectBtn:hover{background:#ef4444!important;color:#fff!important}.empty-state{text-align:center;padding:2rem;color:var(--text-secondary)}.debug-info{background:var(--debug-bg);border:1px solid var(--debug-border);padding:1rem;margin:1rem 0;border-radius:4px;font-family:monospace;font-size:.85rem;white-space:pre-wrap;word-wrap:break-word}.theme-toggle{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:6px;padding:.6rem .8rem;cursor:pointer;font-size:1.1rem;line-height:1;transition:all .2s}.theme-toggle:hover{background:var(--btn-secondary-hover)}[data-theme=dark] #filterBar,[data-theme=dark] #newTaskForm,[data-theme=dark] .welcome div[style*="background: white"]{background:var(--bg-tertiary)!important}[data-theme=dark] input[type=date],[data-theme=dark] input[type=text],[data-theme=dark] select,[data-theme=dark] textarea{background:var(--bg-input)!important;color:var(--text-primary)!important;border-color:var(--border-input)!important}[data-theme=dark] label{color:var(--text-label)!important}[data-theme=dark] small{color:var(--text-secondary)!important}[data-theme=dark] #filterBar{border-bottom-color:var(--border-color)!important}[data-theme=dark] .modal-content{color:var(--text-primary)}@media (max-width:768px){.kanban-board{padding:1rem;gap:1rem;scroll-snap-type:x mandatory}.kanban-column{flex:0 0 85%;min-width:0;scroll-snap-align:start}.modal-content{width:95%;padding:1.25rem}.modal-form-grid{grid-template-columns:1fr!important}}@media (max-width:480px){.kanban-column{flex:0 0 92%}}</style></head><body><header class="header"><div class="header-content"><h1 id="headerTitle">📋 Task Manager</h1><div style="display:flex;gap:.75rem;align-items:center"><select id="languageSelector" onchange="setLanguage(this.value)" style="padding:.6rem 1rem;border:1px solid var(--border-color);border-radius:6px;font-size:.9rem;background:#fff;cursor:pointer"><option value="en">English</option><option value="fr">Français</option><option value="de">Deutsch</option><option value="zh_cn">简体中文</option></select> <select id="projectSelector" style="display:none;padding:.6rem 1rem;border:1px solid var(--border-color);border-radius:6px;font-size:.9rem;background:#fff;cursor:pointer;min-width:180px"></select> <button id="renameProjectBtn" class="btn btn-secondary" style="display:none;padding:.6rem" title="Renommer le projet" onclick="renameCurrentProject()">✏️</button> <button id="deleteProjectBtn" class="btn btn-secondary" style="display:none;padding:.6rem" title="Supprimer le projet" onclick="deleteCurrentProject()">🗑️</button> <button id="selectFolderBtn" class="btn btn-primary">📁 Dossier</button> <button id="newTaskBtn" class="btn btn-secondary" style="display:none">➕ Tâche</button> <button id="archiveBtn" class="btn btn-secondary" style="display:none">📦 Archives</button> <button id="manageColsBtn" class="btn btn-secondary" style="display:none">⚙️ Colonnes</button> <button class="theme-toggle" onclick="toggleTheme()" title="Toggle dark mode" id="themeToggleBtn">🌙</button></div></div></header><div id="filterBar" style="display:none;background:#fff;border-bottom:1px solid var(--border);padding:1rem 0"><div style="max-width:1200px;margin:0 auto;padding:0 2rem"><div style="display:flex;justify-content:center;margin-bottom:1rem"><div style="position:relative;max-width:600px;width:100%"><input type="text" id="globalSearchInput" placeholder="Rechercher dans les tâches..." style="width:100%;padding:.75rem 3rem .75rem 1rem;border:2px solid #cbd5e0;border-radius:8px;font-size:1rem;transition:border-color .2s" oninput="applyGlobalSearch()"> <button id="clearGlobalSearch" onclick="clearGlobalSearch()" style="position:absolute;right:.5rem;top:50%;transform:translateY(-50%);background:0 0;border:none;color:#718096;cursor:pointer;font-size:1.2rem;padding:.5rem;display:none" title="Clear search">✕</button></div></div><div style="display:flex;gap:1rem;align-items:center;flex-wrap:wrap;margin-bottom:.75rem;justify-content:center"><div style="display:flex;gap:.5rem;align-items:center"><label style="font-weight:500;font-size:.9rem">Tags:</label> <select id="filterTagSelect" style="padding:.5rem;border:1px solid #cbd5e0;border-radius:4px;min-width:150px"><option value="">Sélectionner...</option></select> <button onclick='addFilter("tag")' class="btn btn-primary" style="padding:.5rem .75rem;font-size:.85rem">+</button></div><div style="display:flex;gap:.5rem;align-items:center"><label style="font-weight:500;font-size:.9rem">Catégorie:</label> <select id="filterCategorySelect" style="padding:.5rem;border:1px solid #cbd5e0;border-radius:4px;min-width:150px"><option value="">Sélectionner...</option></select> <button onclick='addFilter("category")' class="btn btn-primary" style="padding:.5rem .75rem;font-size:.85rem">+</button></div><div style="display:flex;gap:.5rem;align-items:center"><label style="font-weight:500;font-size:.9rem">Utilisateur:</label> <select id="filterUserSelect" style="padding:.5rem;border:1px solid #cbd5e0;border-radius:4px;min-width:150px"><option value="">Sélectionner...</option></select> <button onclick='addFilter("user")' class="btn btn-primary" style="padding:.5rem .75rem;font-size:.85rem">+</button></div><div style="display:flex;gap:.5rem;align-items:center"><label style="font-weight:500;font-size:.9rem">Priorité:</label> <select id="filterPrioritySelect" style="padding:.5rem;border:1px solid #cbd5e0;border-radius:4px;min-width:150px"><option value="">Sélectionner...</option></select> <button onclick='addFilter("priority")' class="btn btn-primary" style="padding:.5rem .75rem;font-size:.85rem">+</button></div><button onclick="clearFilters()" class="btn btn-secondary" style="padding:.5rem 1rem">✕ Tout effacer</button></div><div id="activeFilters" style="display:flex;gap:.5rem;flex-wrap:wrap;min-height:32px;justify-content:center"></div></div></div><div class="container"><div id="welcomeScreen" class="welcome"><h2>Bienvenue ! 👋</h2><p>Sélectionnez le dossier contenant vos fichiers Markdown (kanban.md et archive.md)</p><button onclick='document.getElementById("selectFolderBtn").click()' class="btn btn-primary" style="font-size:1.1rem;padding:.8rem 2rem">📁 Commencer</button><div style="margin-top:2rem;padding:1.5rem;background:#fff;border-radius:8px;max-width:600px;margin-left:auto;margin-right:auto;text-align:left"><h3 style="margin-bottom:1rem">💡 Comment ça marche ?</h3><ol style="margin-left:1.5rem;color:var(--text-secondary)"><li>Cliquez sur "Commencer" ci-dessus</li><li>Sélectionnez le dossier contenant vos fichiers Markdown</li><li>L'application charge automatiquement kanban.md</li><li>Gérez vos tâches visuellement avec le Kanban</li><li>Les modifications sont sauvegardées dans les fichiers Markdown</li></ol><p style="margin-top:1rem;font-size:.9rem;color:var(--text-secondary)">⚠️ <strong>Navigateurs supportés</strong> : Chrome 86+, Edge 86+, Opera 72+</p></div></div></div><div id="kanbanView" style="display:none"><div id="debugInfo" class="debug-info" style="display:none"></div><div class="kanban-board" id="kanbanBoard"></div></div><div id="taskModal" class="modal"><div class="modal-content"><div class="modal-header"><h2 id="modalTitle">Détails de la tâche</h2><button class="close-btn" onclick="closeModal()">×</button></div><div id="modalBody" class="task-detail"></div><div id="taskModalActions" class="actions"></div></div></div><div id="newTaskModal" class="modal"><div class="modal-content"><div class="modal-header"><h2>Nouvelle tâche</h2><button class="close-btn" onclick="closeTaskModal()">×</button></div><form id="newTaskForm" style="padding:1.5rem;background:#f8f9fa"><input type="hidden" id="taskEditId"><div style="margin-bottom:1rem"><label for="taskTitle" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Titre *</label> <input type="text" id="taskTitle" required style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></div><div style="margin-bottom:1rem"><label for="taskStatus" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Colonne *</label> <select id="taskStatus" required style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></select></div><div class="modal-form-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem"><div><label for="taskPriority" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Priorité</label> <select id="taskPriority" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"><option value="">Aucune</option><option value="Critique">🔴 Critique</option><option value="Haute">🟠 Haute</option><option value="Moyenne">🟡 Moyenne</option><option value="Basse">🟢 Basse</option></select></div><div><label for="taskCategory" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Catégorie</label> <input type="text" id="taskCategory" list="categoriesList" placeholder="Frontend, Backend..." style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></div></div><div style="margin-bottom:1rem"><label for="taskAssignee" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Assigné à</label> <input type="text" id="taskAssignee" list="usersList" placeholder="@alice" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></div><div class="modal-form-grid" style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:1rem;margin-bottom:1rem"><div><label for="taskCreated" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Créé</label> <input type="date" id="taskCreated" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></div><div><label for="taskStarted" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Commencé</label> <input type="date" id="taskStarted" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></div><div><label for="taskDue" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Échéance</label> <input type="date" id="taskDue" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></div><div><label for="taskCompleted" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Terminé</label> <input type="date" id="taskCompleted" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"></div></div><div style="margin-bottom:1rem"><label for="taskTags" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Tags</label> <input type="text" id="taskTags" list="tagsList" placeholder="#bug #feature" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;background:#fff;box-sizing:border-box"> <small style="color:var(--text-secondary);font-size:.85rem">Séparez avec des espaces</small></div><div style="margin-bottom:1rem"><label for="taskDescription" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Description</label> <textarea id="taskDescription" rows="4" style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;resize:vertical;background:#fff;box-sizing:border-box"></textarea></div><div style="margin-bottom:1.5rem"><label style="display:block;margin-bottom:.5rem;font-weight:600;color:var(--text)">Sous-tâches</label><ul id="formSubtasksList" style="list-style:none;padding:0;margin:0 0 .5rem 0;max-height:150px;overflow-y:auto"></ul><div style="display:flex;gap:.5rem"><input type="text" id="formSubtaskInput" placeholder="Ajouter une sous-tâche..." style="flex:1;padding:.5rem;border:2px solid var(--border,#cbd5e0);border-radius:4px;font-size:.9rem;background:var(--bg);color:var(--text)"> <button type="button" onclick="addFormSubtask()" class="btn btn-secondary" style="padding:.5rem 1rem">+ Ajouter</button></div></div><div style="margin-bottom:1.5rem"><label for="taskNotes" style="display:block;margin-bottom:.5rem;font-weight:600;color:#333">Notes</label> <textarea id="taskNotes" rows="6" placeholder="Notes techniques, résultats, décisions, etc. Vous pouvez utiliser du Markdown : **Gras**, *Italique*, `code`, listes, liens..." style="width:100%;padding:.75rem;border:2px solid #cbd5e0;border-radius:6px;font-size:.95rem;resize:vertical;background:#fff;box-sizing:border-box;font-family:monospace"></textarea> <small style="color:var(--text-secondary);font-size:.85rem">Markdown supporté : **gras**, *italique*, `code`, listes, liens, **Sous-sections**:</small></div><div class="actions"><button type="button" class="btn btn-secondary" onclick="closeTaskModal()">Annuler</button> <button type="submit" class="btn btn-primary" id="taskFormSubmitBtn">Créer</button></div></form><datalist id="categoriesList"></datalist><datalist id="usersList"></datalist><datalist id="tagsList"></datalist></div></div><div id="columnsModal" class="modal"><div class="modal-content" style="max-width:500px"><div class="modal-header"><h2 id="columnsModalTitle">Gérer les colonnes</h2><button class="close-btn" onclick="closeColumnsModal()">×</button></div><div style="padding:1.5rem"><div id="columnsList"></div><button id="addColumnBtn" onclick="addColumn()" class="btn btn-primary" style="margin-top:1rem;width:100%">+ Ajouter une colonne</button></div></div></div><div id="archiveModal" class="modal"><div class="modal-content" style="max-width:900px"><div class="modal-header"><h2 id="archiveModalTitle">📦 Archives</h2><button class="close-btn" onclick="closeArchiveModal()">×</button></div><div style="padding:1.5rem"><div style="margin-bottom:1rem"><input type="text" id="archiveSearch" placeholder="Rechercher dans les archives..." style="width:100%;padding:.75rem;border:2px solid var(--border,#cbd5e0);border-radius:6px;font-size:.95rem;background:var(--bg);color:var(--text)"></div><div id="archiveList" style="max-height:500px;overflow-y:auto"></div></div></div></div><div id="notification" class="notification"><span id="notificationText"></span></div><script>function toggleTheme(){const e="dark"===document.documentElement.getAttribute("data-theme")?"light":"dark";document.documentElement.setAttribute("data-theme",e),document.getElementById("themeToggleBtn").textContent="dark"===e?"☀️":"🌙",localStorage.setItem("theme",e)}!function(){const e=localStorage.getItem("theme"),t=window.matchMedia("(prefers-color-scheme: dark)").matches;"dark"===(e||(t?"dark":"light"))&&(document.documentElement.setAttribute("data-theme","dark"),document.addEventListener("DOMContentLoaded",()=>{document.getElementById("themeToggleBtn").textContent="☀️"}))}();let directoryHandle=null,kanbanFileHandle=null,currentKanbanContent="",tasks=[],config={},activeFilters=[],globalSearchTerm="",isEditMode=!1,currentDetailTask=null,formSubtasks=[],archivedTasks=[],archiveFileHandle=null,wasArchiveModalActive=!1,loadedFormatV2=!1,loadedArchiveV2=!1,currentLanguage="en";const translations={en:{"page.title":"Markdown Task Manager","header.title":"📋 Task Manager","header.renameProject":"Rename project","header.deleteProject":"Remove project from list","header.folder":"📁 Folder","header.newTask":"➕ Task","header.archives":"📦 Archives","header.columns":"⚙️ Columns","filters.tags":"Tags:","filters.category":"Category:","filters.user":"User:","filters.priority":"Priority:","filters.select":"Select...","filters.add":"+","filters.clearAll":"✕ Clear all","filters.search":"Search in tasks...","filters.searchClear":"✕","welcome.title":"Welcome! 👋","welcome.description":"Select the folder containing your Markdown files (kanban.md and archive.md)","welcome.start":"📁 Get Started","welcome.howItWorks":"💡 How does it work?","welcome.step1":'Click "Get Started" above',"welcome.step2":"Select the folder containing your Markdown files","welcome.step3":"The app automatically loads kanban.md","welcome.step4":"Manage your tasks visually with Kanban","welcome.step5":"Changes are saved to Markdown files","welcome.browserWarning":"⚠️ Supported browsers: Chrome 86+, Edge 86+, Opera 72+","taskDetail.title":"Task Details","taskDetail.close":"Close","taskDetail.delete":"🗑️ Delete","taskDetail.archive":"📦 Archive","taskDetail.edit":"✏️ Edit","taskForm.newTask":"New Task","taskForm.editTask":"Edit Task","taskForm.titleLabel":"Title *","taskForm.columnLabel":"Column *","taskForm.priorityLabel":"Priority","taskForm.priorityNone":"None","taskForm.priorityCritical":"Critical","taskForm.priorityHigh":"High","taskForm.priorityMedium":"Medium","taskForm.priorityLow":"Low","taskForm.categoryLabel":"Category","taskForm.categoryPlaceholder":"Frontend, Backend...","taskForm.assignedLabel":"Assigned to","taskForm.assignedPlaceholder":"@alice","taskForm.createdLabel":"Created","taskForm.startedLabel":"Started","taskForm.dueLabel":"Due","taskForm.completedLabel":"Completed","taskForm.tagsLabel":"Tags","taskForm.tagsPlaceholder":"#bug #feature","taskForm.tagsHelp":"Separate with spaces","taskForm.descriptionLabel":"Description","taskForm.subtasksLabel":"Subtasks","taskForm.subtaskPlaceholder":"Add a subtask...","taskForm.subtaskAdd":"+ Add","taskForm.notesLabel":"Notes","taskForm.notesPlaceholder":"Technical notes, results, decisions, etc...","taskForm.notesHelp":"Markdown supported: **bold**, *italic*, `code`, lists, links, **Subsections**:","taskForm.cancel":"Cancel","taskForm.create":"Create","taskForm.save":"Save","columns.title":"Manage Columns","columns.add":"+ Add Column","archives.title":"📦 Archives","archives.search":"Search in archives...","archives.empty":"No archived tasks","projects.select":"Select a project...","meta.priority":"Priority","meta.status":"Status","meta.category":"Category","meta.assigned":"Assigned to","meta.created":"Creation date","meta.started":"Start date","meta.due":"Due date","meta.completed":"Completion date","meta.tags":"Tags","meta.description":"Description","meta.subtasks":"Subtasks ({completed}/{total})","meta.notes":"Notes","empty.noTasks":"No tasks","action.restore":"↩️ Restore","action.delete":"🗑️","action.edit":"✏️","action.moveUp":"Move up","action.moveDown":"Move down","tooltip.filterByCategory":"Filter by this category","tooltip.filterByUser":"Filter by this user","tooltip.filterByTag":"Filter by this tag","tooltip.filterByPriority":"Filter by this priority","tooltip.doubleClickEdit":"Double-click to edit","tooltip.delete":"Delete","tooltip.overdue":"Overdue","tooltip.dueSoon":"Due soon","notif.folderLoaded":"Folder loaded successfully!","notif.folderError":"Error loading folder","notif.initializingFolder":"Initializing folder...","notif.filesInitialized":"Files initialized successfully! (kanban.md and archive.md)","notif.filesError":"Error creating files","notif.projectLoaded":'Project "{name}" loaded',"notif.permissionDenied":"Permission denied for this project","notif.projectError":"Error switching project","notif.projectRenamed":"Project renamed successfully","notif.projectDeleted":"Project removed from list","notif.renameError":"Error renaming","notif.projectRestored":"Project restored automatically","notif.taskMoved":"Task moved!","notif.taskEdited":"Task {id} updated!","notif.taskCreated":"Task {id} created!","notif.taskArchived":"Task archived!","notif.taskDeleted":"Task permanently deleted","notif.taskRestored":"Task restored to its original column!","prompt.projectName":'Project name (leave empty to use "{name}"):',"prompt.renameProject":"New project name:","prompt.columnName":"Column name:","prompt.columnId":"Column ID (e.g., todo, done):","prompt.editSubtask":"Edit subtask:","confirm.deleteColumn":"Delete this column?","confirm.deleteSubtask":"Delete this subtask?","confirm.deleteProject":'Remove project "{name}" from the recent list?\n\nThis only removes it from the dropdown - your files will not be deleted.',"confirm.archiveTask":'Archive task "{title}"?',"confirm.deleteTask":'⚠️ WARNING: Permanently delete task "{title}"?\n\nThis action cannot be undone.',"confirm.deleteTaskFromArchive":'⚠️ WARNING: Permanently delete task "{title}"?\n\nThis action cannot be undone.\n\nIf you want to keep it in history, use "Archive" instead.',"alert.browserNotSupported":"Your browser does not support the File System Access API.\n\nPlease use Chrome 86+, Edge 86+ or Opera 72+.","subtask.newPlaceholder":"New subtask...","markdown.archiveTitle":"# Task Archive","markdown.archiveDesc":"> Archived tasks","markdown.archiveSection":"## ✅ Archives","markdown.configSection":"## ⚙️ Configuration","markdown.configColumns":"**Columns**:","markdown.configCategories":"**Categories**:","markdown.configUsers":"**Users**:","markdown.configPriorities":"**Priorities**:","markdown.configTags":"**Tags**:","language.label":"Language:","language.en":"English","language.fr":"Français","language.de":"Deutsch","language.zh_cn":"简体中文"},fr:{"page.title":"Gestionnaire de Tâches Markdown","header.title":"📋 Task Manager","header.renameProject":"Renommer le projet","header.deleteProject":"Retirer le projet de la liste","header.folder":"📁 Dossier","header.newTask":"➕ Tâche","header.archives":"📦 Archives","header.columns":"⚙️ Colonnes","filters.tags":"Tags:","filters.category":"Catégorie:","filters.user":"Utilisateur:","filters.priority":"Priorité:","filters.select":"Sélectionner...","filters.add":"+","filters.clearAll":"✕ Tout effacer","filters.search":"Rechercher dans les tâches...","filters.searchClear":"✕","welcome.title":"Bienvenue ! 👋","welcome.description":"Sélectionnez le dossier contenant vos fichiers Markdown (kanban.md et archive.md)","welcome.start":"📁 Commencer","welcome.howItWorks":"💡 Comment ça marche ?","welcome.step1":'Cliquez sur "Commencer" ci-dessus',"welcome.step2":"Sélectionnez le dossier contenant vos fichiers Markdown","welcome.step3":"L'application charge automatiquement kanban.md","welcome.step4":"Gérez vos tâches visuellement avec le Kanban","welcome.step5":"Les modifications sont sauvegardées dans les fichiers Markdown","welcome.browserWarning":"⚠️ Navigateurs supportés : Chrome 86+, Edge 86+, Opera 72+","taskDetail.title":"Détails de la tâche","taskDetail.close":"Fermer","taskDetail.delete":"🗑️ Supprimer","taskDetail.archive":"📦 Archiver","taskDetail.edit":"✏️ Modifier","taskForm.newTask":"Nouvelle tâche","taskForm.editTask":"Modifier la tâche","taskForm.titleLabel":"Titre *","taskForm.columnLabel":"Colonne *","taskForm.priorityLabel":"Priorité","taskForm.priorityNone":"Aucune","taskForm.priorityCritical":"Critique","taskForm.priorityHigh":"Haute","taskForm.priorityMedium":"Moyenne","taskForm.priorityLow":"Basse","taskForm.categoryLabel":"Catégorie","taskForm.categoryPlaceholder":"Frontend, Backend...","taskForm.assignedLabel":"Assigné à","taskForm.assignedPlaceholder":"@alice","taskForm.createdLabel":"Créé","taskForm.startedLabel":"Commencé","taskForm.dueLabel":"Échéance","taskForm.completedLabel":"Terminé","taskForm.tagsLabel":"Tags","taskForm.tagsPlaceholder":"#bug #feature","taskForm.tagsHelp":"Séparez avec des espaces","taskForm.descriptionLabel":"Description","taskForm.subtasksLabel":"Sous-tâches","taskForm.subtaskPlaceholder":"Ajouter une sous-tâche...","taskForm.subtaskAdd":"+ Ajouter","taskForm.notesLabel":"Notes","taskForm.notesPlaceholder":"Notes techniques, résultats, décisions, etc...","taskForm.notesHelp":"Markdown supporté : **gras**, *italique*, `code`, listes, liens, **Sous-sections**:","taskForm.cancel":"Annuler","taskForm.create":"Créer","taskForm.save":"Enregistrer","columns.title":"Gérer les colonnes","columns.add":"+ Ajouter une colonne","archives.title":"📦 Archives","archives.search":"Rechercher dans les archives...","archives.empty":"Aucune tâche archivée","projects.select":"Sélectionner un projet...","meta.priority":"Priorité","meta.status":"Statut","meta.category":"Catégorie","meta.assigned":"Assigné à","meta.created":"Date de création","meta.started":"Date de début","meta.due":"Date d'échéance","meta.completed":"Date de fin","meta.tags":"Tags","meta.description":"Description","meta.subtasks":"Sous-tâches ({completed}/{total})","meta.notes":"Notes","empty.noTasks":"Aucune tâche","action.restore":"↩️ Restaurer","action.delete":"🗑️","action.edit":"✏️","action.moveUp":"Déplacer vers le haut","action.moveDown":"Déplacer vers le bas","tooltip.filterByCategory":"Filtrer par cette catégorie","tooltip.filterByUser":"Filtrer par cet utilisateur","tooltip.filterByTag":"Filtrer par ce tag","tooltip.filterByPriority":"Filtrer par cette priorité","tooltip.doubleClickEdit":"Double-cliquez pour éditer","tooltip.delete":"Supprimer","tooltip.overdue":"En retard","tooltip.dueSoon":"Bientôt dû","notif.folderLoaded":"Dossier chargé avec succès !","notif.folderError":"Erreur lors de la sélection du dossier","notif.initializingFolder":"Initialisation du dossier...","notif.filesInitialized":"Fichiers initialisés avec succès ! (kanban.md et archive.md)","notif.filesError":"Erreur lors de la création des fichiers","notif.projectLoaded":'Projet "{name}" chargé',"notif.permissionDenied":"Permission refusée pour ce projet","notif.projectError":"Erreur lors du changement de projet","notif.projectRenamed":"Projet renommé avec succès","notif.projectDeleted":"Projet retiré de la liste","notif.renameError":"Erreur lors du renommage","notif.projectRestored":"Projet restauré automatiquement","notif.taskMoved":"Tâche déplacée !","notif.taskEdited":"Tâche {id} modifiée !","notif.taskCreated":"Tâche {id} créée !","notif.taskArchived":"Tâche archivée !","notif.taskDeleted":"Tâche supprimée définitivement","notif.taskRestored":"Tâche restaurée dans sa colonne d'origine !","prompt.projectName":'Nom du projet (laisser vide pour utiliser "{name}") :',"prompt.renameProject":"Nouveau nom du projet :","prompt.columnName":"Nom de la colonne:","prompt.columnId":"ID de la colonne (ex: todo, done):","prompt.editSubtask":"Modifier la sous-tâche:","confirm.deleteColumn":"Supprimer cette colonne ?","confirm.deleteSubtask":"Supprimer cette sous-tâche ?","confirm.deleteProject":'Retirer le projet "{name}" de la liste récente ?\n\nCeci retire seulement le projet du menu déroulant - vos fichiers ne seront pas supprimés.',"confirm.archiveTask":'Archiver la tâche "{title}" ?',"confirm.deleteTask":'⚠️ ATTENTION : Supprimer définitivement la tâche "{title}" ?\n\nCette action est irréversible.',"confirm.deleteTaskFromArchive":'⚠️ ATTENTION : Supprimer définitivement la tâche "{title}" ?\n\nCette action est irréversible.\n\nSi vous voulez la conserver dans l\'historique, utilisez plutôt "Archiver".',"alert.browserNotSupported":"Votre navigateur ne supporte pas la File System Access API.\n\nVeuillez utiliser Chrome 86+, Edge 86+ ou Opera 72+.","subtask.newPlaceholder":"Nouvelle sous-tâche...","markdown.archiveTitle":"# Archive des Tâches","markdown.archiveDesc":"> Tâches archivées","markdown.archiveSection":"## ✅ Archives","markdown.configSection":"## ⚙️ Configuration","markdown.configColumns":"**Colonnes**:","markdown.configCategories":"**Catégories**:","markdown.configUsers":"**Utilisateurs**:","markdown.configPriorities":"**Priorités**:","markdown.configTags":"**Tags**:","language.label":"Langue :","language.en":"English","language.fr":"Français","language.de":"Deutsch","language.zh_cn":"简体中文"},de:{"page.title":"Markdown Task Manager","header.title":"📋 Task Manager","header.renameProject":"Projekt umbenennen","header.deleteProject":"Projekt aus Liste entfernen","header.folder":"📁 Ordner","header.newTask":"➕ Aufgabe","header.archives":"📦 Archiv","header.columns":"⚙️ Spalten","filters.tags":"Tags:","filters.category":"Kategorie:","filters.user":"Benutzer:","filters.priority":"Priorität:","filters.select":"Auswählen...","filters.add":"+","filters.clearAll":"✕ Alles löschen","filters.search":"In Aufgaben suchen...","filters.searchClear":"✕","welcome.title":"Willkommen! 👋","welcome.description":"Wählen Sie den Ordner aus, der die Markdown-Dateien beinhaltet (kanban.md and archive.md)","welcome.start":"📁 Loslegen","welcome.howItWorks":"💡 Wie funktioniert es?","welcome.step1":'Klicken Sie oben auf "Loslegen"',"welcome.step2":"Wählen Sie den Ordner aus, der die Markdown-Dateien beinhaltet","welcome.step3":"Die App lädt automatisch kanban.md","welcome.step4":"Verwalten Sie Ihre Aufgaben grafisch mit Kanban","welcome.step5":"Änderungen werden als Markdown-Datei gespeichert","welcome.browserWarning":"⚠️ Unterstützte Browser: Chrome 86+, Edge 86+, Opera 72+","taskDetail.title":"Aufgabendetails","taskDetail.close":"Schließen","taskDetail.delete":"🗑️ Löschen","taskDetail.archive":"📦 Archivieren","taskDetail.edit":"✏️ Bearbeiten","taskForm.newTask":"Neue Aufgabe","taskForm.editTask":"Aufgabe bearbeiten","taskForm.titleLabel":"Titel *","taskForm.columnLabel":"Spalte *","taskForm.priorityLabel":"Priorität","taskForm.priorityNone":"Ohne","taskForm.priorityCritical":"Kritisch","taskForm.priorityHigh":"Hoch","taskForm.priorityMedium":"Mittel","taskForm.priorityLow":"Niedrig","taskForm.categoryLabel":"Kategorie","taskForm.categoryPlaceholder":"Frontend, Backend...","taskForm.assignedLabel":"Zugewiesen an","taskForm.assignedPlaceholder":"@alice","taskForm.createdLabel":"Erstellt","taskForm.startedLabel":"Begonnen","taskForm.dueLabel":"Fällig","taskForm.completedLabel":"Abgeschlossen","taskForm.tagsLabel":"Tags","taskForm.tagsPlaceholder":"#bug #feature","taskForm.tagsHelp":"Mit Leerzeichen trennen","taskForm.descriptionLabel":"Beschreibung","taskForm.subtasksLabel":"Unteraufgaben","taskForm.subtaskPlaceholder":"Unteraufgabe hinzufügen...","taskForm.subtaskAdd":"+ Hinzufügen","taskForm.notesLabel":"Anmerkungen","taskForm.notesPlaceholder":"Technische Anmerkungen, Ergebnisse, Entscheidungen, etc...","taskForm.notesHelp":"Markdown-Unterstützung: **fett**, *kursiv*, `code`, Listen, Links, **Abschnitte**:","taskForm.cancel":"Abbrechen","taskForm.create":"Erstellen","taskForm.save":"Speichern","columns.title":"Spalten bearbeiten","columns.add":"+ Spalte hinzufügen","archives.title":"📦 Archiv","archives.search":"Suche im Archiv...","archives.empty":"Keine archivierten Aufgaben","projects.select":"Wählen Sie ein Projekt...","meta.priority":"Priorität","meta.status":"Status","meta.category":"Kategorie","meta.assigned":"Zugewiesen an","meta.created":"Erstellungsdatum","meta.started":"Startdatum","meta.due":"Fälligkeitsdatum","meta.completed":"Erledigungsdatum","meta.tags":"Tags","meta.description":"Beschreibung","meta.subtasks":"Unteraufgaben ({completed}/{total})","meta.notes":"Anmerkungen","empty.noTasks":"Keine Aufgaben","action.restore":"↩️ Wiederherstellen","action.delete":"🗑️","action.edit":"✏️","action.moveUp":"Nach oben","action.moveDown":"Nach unten","tooltip.filterByCategory":"Nach dieser Kategorie filtern","tooltip.filterByUser":"Nach diesem Benutzer filtern","tooltip.filterByTag":"Nach diesem Tag filtern","tooltip.filterByPriority":"Nach dieser Priorität filtern","tooltip.doubleClickEdit":"Doppelklick zum Bearbeiten","tooltip.delete":"Löschen","tooltip.overdue":"Überfällig","tooltip.dueSoon":"Bald fällig","notif.folderLoaded":"Ordner erfolgreich geladen!","notif.folderError":"Problem beim Laden des Ordners","notif.initializingFolder":"Ordner wird initialisiert...","notif.filesInitialized":"Dateien erfolgreich initialisiert! (kanban.md and archive.md)","notif.filesError":"Problem beim Erstellen der Dateien","notif.projectLoaded":'Projekt "{name}" geladen',"notif.permissionDenied":"Zugriff verweigert für dieses Projekt","notif.projectError":"Problem beim Wechseln des Projekts","notif.projectRenamed":"Projekt erfolgreich umbenannt","notif.projectDeleted":"Projekt aus Liste gelöscht","notif.renameError":"Problem beim Umbenennen","notif.projectRestored":"Projekt erfolgreich wiederhergestellt","notif.taskMoved":"Aufgabe verschoben!","notif.taskEdited":"Aufgabe {id} aktualisiert!","notif.taskCreated":"Aufgabe {id} erstellt!","notif.taskArchived":"Aufgabe archiviert!","notif.taskDeleted":"Aufgabe endgültig gelöscht","notif.taskRestored":"Aufgabe in ursprünglicher Spalte zurückgesetzt!","prompt.projectName":'Projektname (leer lassen, um "{name}" zu verwenden):',"prompt.renameProject":"Neuer Projektname:","prompt.columnName":"Spaltenname:","prompt.columnId":"Spalten-ID (z.B., todo, done):","prompt.editSubtask":"Unteraufgabe bearbeiten:","confirm.deleteColumn":"Diese Spalte löschen?","confirm.deleteSubtask":"Diese Unteraufgabe löschen?","confirm.deleteProject":'Projekt "{name}" aus Liste löschen?\n\nDas Projekt wird nur aus der Liste gelöscht. Die Dateien bleiben bestehen.',"confirm.archiveTask":'Aufgabe "{title}" archivieren?',"confirm.deleteTask":'⚠️ WARNUNG: Aufgabe "{title}" endgültig löschen?\n\nDies kann nicht rückgängig gemacht werden.',"confirm.deleteTaskFromArchive":'⚠️ WARNUNG: Aufgabe "{title}" endgültig löschen?\n\nDies kann nicht rückgängig gemacht werden.\n\nWenn die Aufgabe in der Historie vorgehalten werden soll, klicken Sie stattdessen auf "Archivieren".',"alert.browserNotSupported":"Ihr Browser unterstützt die File System Access API nicht.\nBitte Chrome 86+, Edge 86+ oder Opera 72+ nutzen.","subtask.newPlaceholder":"Neue Unteraufgabe...","markdown.archiveTitle":"# Aufgaben-Archiv","markdown.archiveDesc":"> Archivierte Aufgaben","markdown.archiveSection":"## ✅ Archiv","markdown.configSection":"## ⚙️ Konfiguration","markdown.configColumns":"**Spalten**:","markdown.configCategories":"**Kategorien**:","markdown.configUsers":"**Benutzer**:","markdown.configPriorities":"**Prioritäten**:","markdown.configTags":"**Tags**:","language.label":"Sprache:","language.en":"English","language.fr":"Français","language.de":"Deutsch"},zh_cn:{"page.title":"Markdown 任务管理器","header.title":"📋 任务管理器","header.renameProject":"重命名项目","header.deleteProject":"从列表中移除项目","header.folder":"📁 文件夹","header.newTask":"➕ 任务","header.archives":"📦 归档","header.columns":"⚙️ 列设置","filters.tags":"标签:","filters.category":"类别:","filters.user":"用户:","filters.priority":"优先级:","filters.select":"选择...","filters.add":"+","filters.clearAll":"✕ 清空全部","filters.search":"在任务中搜索...","filters.searchClear":"✕","welcome.title":"欢迎! 👋","welcome.description":"选择包含您 Markdown 文件的文件夹 (kanban.md 和 archive.md)","welcome.start":"📁 开始使用","welcome.howItWorks":"💡 工作原理?","welcome.step1":'点击上方的"开始使用"',"welcome.step2":"选择包含您 Markdown 文件的文件夹","welcome.step3":"应用将自动加载 kanban.md","welcome.step4":"通过看板可视化地管理任务","welcome.step5":"更改将保存到 Markdown 文件中","welcome.browserWarning":"⚠️ 支持的浏览器: Chrome 86+, Edge 86+, Opera 72+","taskDetail.title":"任务详情","taskDetail.close":"关闭","taskDetail.delete":"🗑️ 删除","taskDetail.archive":"📦 归档","taskDetail.edit":"✏️ 编辑","taskForm.newTask":"新建任务","taskForm.editTask":"编辑任务","taskForm.titleLabel":"标题 *","taskForm.columnLabel":"列 *","taskForm.priorityLabel":"优先级","taskForm.priorityNone":"无","taskForm.priorityCritical":"紧急","taskForm.priorityHigh":"高","taskForm.priorityMedium":"中","taskForm.priorityLow":"低","taskForm.categoryLabel":"类别","taskForm.categoryPlaceholder":"前端、后端...","taskForm.assignedLabel":"分配给","taskForm.assignedPlaceholder":"@用户名","taskForm.createdLabel":"创建于","taskForm.startedLabel":"开始于","taskForm.dueLabel":"截止于","taskForm.completedLabel":"完成于","taskForm.tagsLabel":"标签","taskForm.tagsPlaceholder":"#bug #feature","taskForm.tagsHelp":"用空格分隔","taskForm.descriptionLabel":"描述","taskForm.subtasksLabel":"子任务","taskForm.subtaskPlaceholder":"添加子任务...","taskForm.subtaskAdd":"+ 添加","taskForm.notesLabel":"备注","taskForm.notesPlaceholder":"技术备注、结果、决策等...","taskForm.notesHelp":"Markdown 支持: **粗体**, *斜体*, `代码`, 列表, 链接, **子节标题**:","taskForm.cancel":"取消","taskForm.create":"创建","taskForm.save":"保存","columns.title":"管理列","columns.add":"+ 添加列","archives.title":"📦 归档","archives.search":"在归档中搜索...","archives.empty":"无已归档任务","projects.select":"选择一个项目...","meta.priority":"优先级","meta.status":"状态","meta.category":"类别","meta.assigned":"分配给","meta.created":"创建日期","meta.started":"开始日期","meta.due":"截止日期","meta.completed":"完成日期","meta.tags":"标签","meta.description":"描述","meta.subtasks":"子任务 ({completed}/{total})","meta.notes":"备注","empty.noTasks":"暂无任务","action.restore":"↩️ 恢复","action.delete":"🗑️","action.edit":"✏️","action.moveUp":"上移","action.moveDown":"下移","tooltip.filterByCategory":"按此类别筛选","tooltip.filterByUser":"按此用户筛选","tooltip.filterByTag":"按此标签筛选","tooltip.filterByPriority":"按此优先级筛选","tooltip.doubleClickEdit":"双击编辑","tooltip.delete":"删除","tooltip.overdue":"已逾期","tooltip.dueSoon":"即将到期","notif.folderLoaded":"文件夹加载成功!","notif.folderError":"加载文件夹时出错","notif.initializingFolder":"正在初始化文件夹...","notif.filesInitialized":"文件初始化成功! (kanban.md 和 archive.md)","notif.filesError":"创建文件时出错","notif.projectLoaded":'项目 "{name}" 已加载',"notif.permissionDenied":"无此项目的访问权限","notif.projectError":"切换项目时出错","notif.projectRenamed":"项目重命名成功","notif.projectDeleted":"项目已从列表中移除","notif.renameError":"重命名时出错","notif.projectRestored":"项目已自动恢复","notif.taskMoved":"任务已移动!","notif.taskEdited":"任务 {id} 已更新!","notif.taskCreated":"任务 {id} 已创建!","notif.taskArchived":"任务已归档!","notif.taskDeleted":"任务已永久删除","notif.taskRestored":"任务已恢复至原列!","prompt.projectName":'项目名称 (留空则使用 "{name}"):',"prompt.renameProject":"新项目名称:","prompt.columnName":"列名称:","prompt.columnId":"列 ID (例如: todo, done):","prompt.editSubtask":"编辑子任务:","confirm.deleteColumn":"删除此列?","confirm.deleteSubtask":"删除此子任务?","confirm.deleteProject":'从近期列表中移除项目 "{name}" ?\n\n此操作仅会将其从下拉列表中移除, 您的文件不会被删除.',"confirm.archiveTask":'归档任务 "{title}"?',"confirm.deleteTask":'⚠️ 警告: 永久删除任务 "{title}"?\n\n此操作无法撤销.',"confirm.deleteTaskFromArchive":'⚠️ 警告: 永久删除任务 "{title}"?\n\n此操作无法撤销.\n\n若希望保留历史记录, 请改用“归档”功能.',"alert.browserNotSupported":"您的浏览器不支持文件系统访问 API.\n\n请使用 Chrome 86+、 Edge 86+ 或 Opera 72+.","subtask.newPlaceholder":"新的子任务...","markdown.archiveTitle":"# 任务归档","markdown.archiveDesc":"> 已归档的任务","markdown.archiveSection":"## ✅ 归档","markdown.configSection":"## ⚙️ 配置","markdown.configColumns":"**列**:","markdown.configCategories":"**类别**:","markdown.configUsers":"**用户**:","markdown.configPriorities":"**优先级**:","markdown.configTags":"**标签**:","language.label":"语言:","language.en":"English","language.fr":"Français","language.zh_cn":"简体中文"}},priorityIconClasses={"🟢":"Green","🟡":"Yellow","🟠":"Orange","🔴":"Red","🔵":"Blue","🟣":"Purple","⚪":"White","⚫":"Black","❤️":"Red","🧡":"Orange","💛":"Yellow","💚":"Green","💙":"Blue","💜":"Purple","🤍":"White","🖤":"Black","🟥":"Red","🟧":"Orange","🟨":"Yellow","🟩":"Green","🟦":"Blue","🟪":"Purple","🔶":"Orange","🔷":"Blue","🔸":"Orange","🔹":"Blue","⭐":"Yellow","🌟":"Yellow","🚩":"Red","🏴":"Black","🏳️":"White","⚠️":"Yellow","🔥":"Orange","💥":"Red","⚡":"Yellow","⬆️":"Red","➡️":"Blue","⬇️":"Green","❗":"Red","❓":"Blue","❕":"Red","❔":"Blue"};function t(e,t={}){let n=translations[currentLanguage]?.[e]||translations.en[e]||e;return Object.keys(t).forEach(e=>{n=n.replace(`{${e}}`,t[e])}),n}function clean(e){return e.replace(/[^\w\s]+/,"").trim()}function getFirstEmoji(e){const t=e.match(/[^\w\s]+/);return t&&[...t[0]][0]||""}function displayPriority(e){if(!e||"en"===currentLanguage)return e;const n=getFirstEmoji(e),a=clean(e),o={Critical:"taskForm.priorityCritical",High:"taskForm.priorityHigh",Medium:"taskForm.priorityMedium",Low:"taskForm.priorityLow"};if(o[a]){return n+(n?" ":"")+t(o[a])}return e}function updateStaticTexts(){document.title=t("page.title"),document.getElementById("headerTitle").textContent=t("header.title"),document.getElementById("renameProjectBtn").title=t("header.renameProject"),document.getElementById("deleteProjectBtn").title=t("header.deleteProject"),document.getElementById("selectFolderBtn").innerHTML=t("header.folder"),document.getElementById("newTaskBtn").innerHTML=t("header.newTask"),document.getElementById("archiveBtn").innerHTML=t("header.archives"),document.getElementById("manageColsBtn").innerHTML=t("header.columns"),document.getElementById("languageSelector").value=currentLanguage,renderWelcomeScreen(),renderTaskDetailModal(),updateTaskFormLabels(),renderColumnsModal(),renderArchivesModal(),updateFilterLabels()}function renderWelcomeScreen(){const e=document.getElementById("welcomeScreen");e&&(e.innerHTML=`\n <h2>${t("welcome.title")}</h2>\n <p>${t("welcome.description")}</p>\n <button onclick="document.getElementById('selectFolderBtn').click()" class="btn btn-primary" style="font-size: 1.1rem; padding: 0.8rem 2rem;">\n ${t("welcome.start")}\n </button>\n <div style="margin-top: 2rem; padding: 1.5rem; background: white; border-radius: 8px; max-width: 600px; margin-left: auto; margin-right: auto; text-align: left;">\n <h3 style="margin-bottom: 1rem;">${t("welcome.howItWorks")}</h3>\n <ol style="margin-left: 1.5rem; color: var(--text-secondary);">\n <li>${t("welcome.step1")}</li>\n <li>${t("welcome.step2")}</li>\n <li>${t("welcome.step3")}</li>\n <li>${t("welcome.step4")}</li>\n <li>${t("welcome.step5")}</li>\n </ol>\n <p style="margin-top: 1rem; font-size: 0.9rem; color: var(--text-secondary);">\n ${t("welcome.browserWarning")}\n </p>\n </div>\n `)}function renderTaskDetailModal(){const e=document.getElementById("modalTitle");e&&(e.textContent=t("taskDetail.title"));const n=document.getElementById("taskModal");if(n){const e=n.querySelector(".actions");e&&(e.innerHTML=`\n <button class="btn btn-secondary" onclick="closeModal()">${t("taskDetail.close")}</button>\n <button class="btn btn-secondary" onclick="deleteCurrentTask()" style="background: #ef4444; color: white;">${t("taskDetail.delete")}</button>\n <button class="btn btn-secondary" onclick="archiveCurrentTask()" style="background: #f59e0b; color: white;">${t("taskDetail.archive")}</button>\n <button class="btn btn-primary" id="editTaskBtn" onclick="editCurrentTask()">${t("taskDetail.edit")}</button>\n `)}}function updateTaskFormLabels(){const e={taskTitle:{prev:"label",text:t("taskForm.titleLabel")},taskStatus:{prev:"label",text:t("taskForm.columnLabel")},taskPriority:{prev:"label",text:t("taskForm.priorityLabel")},taskCategory:{prev:"label",text:t("taskForm.categoryLabel"),placeholder:t("taskForm.categoryPlaceholder")},taskAssignee:{prev:"label",text:t("taskForm.assignedLabel"),placeholder:t("taskForm.assignedPlaceholder")},taskCreated:{prev:"label",text:t("taskForm.createdLabel")},taskStarted:{prev:"label",text:t("taskForm.startedLabel")},taskDue:{prev:"label",text:t("taskForm.dueLabel")},taskCompleted:{prev:"label",text:t("taskForm.completedLabel")},taskTags:{prev:"label",text:t("taskForm.tagsLabel"),placeholder:t("taskForm.tagsPlaceholder")},taskDescription:{prev:"label",text:t("taskForm.descriptionLabel")},taskNotes:{prev:"label",text:t("taskForm.notesLabel"),placeholder:t("taskForm.notesPlaceholder")},formSubtaskInput:{placeholder:t("taskForm.subtaskPlaceholder")}};Object.keys(e).forEach(t=>{const n=document.getElementById(t);if(!n)return;const a=e[t];if("label"===a.prev){const e=n.previousElementSibling;if(e&&"LABEL"===e.tagName)e.textContent=a.text;else{const e=document.querySelector(`label[for="${t}"]`);e&&(e.textContent=a.text)}}a.placeholder&&(n.placeholder=a.placeholder)});const n=document.getElementById("taskPriority");n&&(n.innerHTML=`\n <option value="">${t("taskForm.priorityNone")}</option>\n <option value="Critical">${t("taskForm.priorityCritical")}</option>\n <option value="High">${t("taskForm.priorityHigh")}</option>\n <option value="Medium">${t("taskForm.priorityMedium")}</option>\n <option value="Low">${t("taskForm.priorityLow")}</option>\n `);const a=document.querySelector('label[for="taskTags"] + input + small');a&&(a.textContent=t("taskForm.tagsHelp"));const o=document.querySelector("#taskNotes + small");o&&(o.textContent=t("taskForm.notesHelp"));const r=document.querySelector("#formSubtasksList").previousElementSibling;r&&(r.textContent=t("taskForm.subtasksLabel"));const s=document.querySelector("#formSubtaskInput + button");s&&(s.textContent=t("taskForm.subtaskAdd"));const i=document.querySelector('#newTaskForm .actions button[type="button"]');i&&(i.textContent=t("taskForm.cancel"))}function updateFilterLabels(){const e=document.getElementById("filterBar");if(!e)return;const n=document.getElementById("globalSearchInput");n&&(n.placeholder=t("filters.search"));const a=e.querySelectorAll("label");a[0]&&(a[0].textContent=t("filters.tags")),a[1]&&(a[1].textContent=t("filters.category")),a[2]&&(a[2].textContent=t("filters.user")),a[3]&&(a[3].textContent=t("filters.priority"));e.querySelectorAll('select option[value=""]').forEach(e=>e.textContent=t("filters.select"));const o=e.querySelector('button[onclick="clearFilters()"]');o&&(o.textContent=t("filters.clearAll"))}function renderColumnsModal(){const e=document.getElementById("columnsModalTitle");e&&(e.textContent=t("columns.title"));const n=document.getElementById("addColumnBtn");n&&(n.textContent=t("columns.add"))}function renderArchivesModal(){const e=document.getElementById("archiveModalTitle");e&&(e.textContent=t("archives.title"));const n=document.getElementById("archiveSearch");n&&(n.placeholder=t("archives.search"))}function setLanguage(e){translations[e]||(console.warn(`Language "${e}" not available, falling back to English`),e="en"),currentLanguage=e,localStorage.setItem("preferredLanguage",e),updateStaticTexts(),updateAutocomplete(),renderKanban(),updateProjectSelector(),renderArchivesModal(),console.log(`Language changed to: ${e}`)}function initLanguage(){const e=localStorage.getItem("preferredLanguage");if(e)return void(currentLanguage=e);const t=(navigator.language||navigator.userLanguage).toLowerCase().split("-")[0];currentLanguage=translations[t]?t:"en",localStorage.setItem("preferredLanguage",currentLanguage),console.log(`Language initialized to: ${currentLanguage}`)}initLanguage(),document.addEventListener("DOMContentLoaded",updateStaticTexts),"showDirectoryPicker"in window||alert(t("alert.browserNotSupported"));const DB_NAME="TaskManagerDB",DB_VERSION=2,STORE_NAME="settings",PROJECTS_KEY="recentProjects",MAX_RECENT_PROJECTS=10;async function openDB(){return new Promise((e,t)=>{const n=indexedDB.open(DB_NAME,2);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=e=>{const t=e.target.result;t.objectStoreNames.contains("settings")||t.createObjectStore("settings")}})}async function saveDirectoryHandle(e,n=null){try{const a=(await openDB()).transaction("settings","readwrite"),o=a.objectStore("settings"),r=o.get(PROJECTS_KEY),s=await new Promise((e,t)=>{r.onsuccess=()=>e(r.result||[]),r.onerror=()=>t(r.error)}),i=e.name,l=s.findIndex(e=>e.name===i);let c=n;l<0&&!n&&(c=prompt(t("prompt.projectName",{name:i}))||"");const d={handle:e,name:i,customName:c||(l>=0?s[l].customName:""),lastAccessed:Date.now()};l>=0?(d.customName=null!==n?n:s[l].customName,s[l]=d):s.unshift(d);const m=s.slice(0,10);o.put(m,PROJECTS_KEY),await new Promise((e,t)=>{a.oncomplete=e,a.onerror=()=>t(a.error)}),console.log("Project saved to recent list:",d.customName||i),updateProjectSelector()}catch(e){console.error("Failed to save directory handle:",e)}}async function loadRecentProjects(){try{const e=(await openDB()).transaction("settings","readonly"),t=e.objectStore("settings").get(PROJECTS_KEY);return new Promise((e,n)=>{t.onsuccess=()=>e(t.result||[]),t.onerror=()=>n(t.error)})}catch(e){return console.error("Failed to load recent projects:",e),[]}}async function loadDirectoryHandle(){try{const e=await loadRecentProjects();return e.length>0?e[0].handle:null}catch(e){return console.error("Failed to load directory handle:",e),null}}async function verifyPermission(e){const t={mode:"readwrite"};return"granted"===await e.queryPermission(t)||"granted"===await e.requestPermission(t)}async function updateProjectSelector(){const e=await loadRecentProjects(),n=document.getElementById("projectSelector"),a=document.getElementById("renameProjectBtn"),o=document.getElementById("deleteProjectBtn");if(e.length>0){n.style.display="block";const r="none"!==document.getElementById("kanbanView").style.display,s=r&&directoryHandle?directoryHandle.name:"";r?(a.style.display="inline-flex",o.style.display="inline-flex"):(a.style.display="none",o.style.display="none");let i="";r||(i=`<option value="" selected>${t("projects.select")}</option>`),i+=e.map((e,t)=>{const n=r&&e.name===s?"selected":"",a=new Date(e.lastAccessed).toLocaleDateString("fr-FR");return`<option value="${t}" ${n}>${e.customName||e.name}${e.customName?` (📁 ${e.name})`:""} - ${a}</option>`}).join(""),n.innerHTML=i}else n.style.display="none",a.style.display="none",o.style.display="none"}async function switchProject(e){try{const n=await loadRecentProjects();if(e>=0&&e<n.length){const a=n[e];await verifyPermission(a.handle)?(directoryHandle=a.handle,await saveDirectoryHandle(directoryHandle),await loadKanbanFile(),document.getElementById("welcomeScreen").style.display="none",document.getElementById("kanbanView").style.display="block",document.getElementById("filterBar").style.display="block",document.getElementById("newTaskBtn").style.display="inline-flex",document.getElementById("archiveBtn").style.display="inline-flex",document.getElementById("manageColsBtn").style.display="inline-flex",await updateProjectSelector(),showNotification(t("notif.projectLoaded",{name:a.customName||a.name}),"success")):showNotification(t("notif.permissionDenied"),"error")}}catch(e){console.error("Error switching project:",e),showNotification(t("notif.projectError"),"error")}}async function renameCurrentProject(){if(directoryHandle)try{const e=(await loadRecentProjects()).find(e=>e.name===directoryHandle.name);if(e){const n=e.customName||e.name,a=prompt(t("prompt.renameProject"),n);null!==a&&a.trim()!==n&&(await saveDirectoryHandle(directoryHandle,a.trim()),showNotification(t("notif.projectRenamed"),"success"))}}catch(e){console.error("Error renaming project:",e),showNotification(t("notif.renameError"),"error")}}async function deleteCurrentProject(){if(directoryHandle)try{const e=await loadRecentProjects(),n=e.find(e=>e.name===directoryHandle.name);if(n){const a=n.customName||n.name;if(confirm(t("confirm.deleteProject",{name:a}))){const n=e.filter(e=>e.name!==directoryHandle.name),a=(await openDB()).transaction("settings","readwrite");a.objectStore("settings").put(n,PROJECTS_KEY),await new Promise((e,t)=>{a.oncomplete=e,a.onerror=()=>t(a.error)}),directoryHandle=null,kanbanFileHandle=null,tasks=[],document.getElementById("kanbanView").style.display="none",document.getElementById("filterBar").style.display="none",document.getElementById("newTaskBtn").style.display="none",document.getElementById("archiveBtn").style.display="none",document.getElementById("manageColsBtn").style.display="none",document.getElementById("welcomeScreen").style.display="block",await updateProjectSelector(),showNotification(t("notif.projectDeleted"),"success")}}}catch(e){console.error("Error deleting project:",e),showNotification(t("notif.renameError"),"error")}}async function tryRestorePreviousDirectory(){const e=await loadDirectoryHandle();if(console.log("Attempting to restore previous directory:",e?e.name:"none"),e)try{if(console.log("Verifying permissions..."),await verifyPermission(e))return console.log("Permission granted, loading kanban file..."),directoryHandle=e,await loadKanbanFile(),document.getElementById("welcomeScreen").style.display="none",document.getElementById("kanbanView").style.display="block",document.getElementById("filterBar").style.display="block",document.getElementById("newTaskBtn").style.display="inline-flex",document.getElementById("archiveBtn").style.display="inline-flex",document.getElementById("manageColsBtn").style.display="inline-flex",showNotification(t("notif.projectRestored"),"success"),await updateProjectSelector(),console.log("Project restored successfully"),!0;console.log("Permission denied or cancelled")}catch(e){console.error("Could not restore previous directory:",e)}return await updateProjectSelector(),!1}async function loadKanbanFile(){try{kanbanFileHandle=await directoryHandle.getFileHandle("kanban.md");const e=await kanbanFileHandle.getFile();currentKanbanContent=await e.text(),console.log("File loaded, size:",currentKanbanContent.length),parseMarkdown(currentKanbanContent),await loadArchive(),updateAutocomplete(),renderKanban();const t=(currentKanbanContent.match(/^###\s+TASK-/gm)||[]).length,n=new Set(tasks.map(e=>e.id)).size;loadedFormatV2||tasks.length!==t||n!==t?loadedFormatV2||console.warn(`Skipped V1→V2 migration: incomplete parse (${tasks.length}/${t} task blocks). File left unchanged.`):await backupOriginal("kanban.md",currentKanbanContent)&&await autoSave()?console.log("Migrated kanban.md from V1 (sections) to V2 (status field)"):console.warn("Skipped V1→V2 migration: backup or save failed. File left unchanged.")}catch(e){if("NotFoundError"===e.name){showNotification(t("notif.initializingFolder"),"success"),console.log("kanban.md not found, creating default files...");try{currentKanbanContent=createDefaultKanbanContent(),kanbanFileHandle=await directoryHandle.getFileHandle("kanban.md",{create:!0});const e=await kanbanFileHandle.createWritable();await e.write(currentKanbanContent),await e.close();const n=createDefaultArchiveContent(),a=await directoryHandle.getFileHandle("archive.md",{create:!0}),o=await a.createWritable();await o.write(n),await o.close(),showNotification(t("notif.filesInitialized"),"success"),parseMarkdown(currentKanbanContent),updateAutocomplete(),renderKanban()}catch(e){showNotification(t("notif.filesError"),"error"),console.error(e)}}else showNotification(t("notif.loadError")||"Erreur lors du chargement du fichier","error"),console.error("Error loading kanban.md:",e)}}function createDefaultKanbanContent(){return"# Kanban Board\n\n\x3c!-- Config: Last Task ID: 0 --\x3e\n\x3c!-- Format: v2 --\x3e\n\n## ⚙️ Configuration\n\n**Columns**: 📝 To Do (todo) | 🚀 In Progress (in-progress) | 👀 In Review (in-review) | ✅ Done (done)\n\n**Categories**: Frontend, Backend, Design, DevOps, Tests, Documentation\n\n**Users**: @user (User)\n\n**Priorities**: 🔴 Critical | 🟠 High | 🟡 Medium | 🟢 Low\n\n**Tags**: #bug #feature #ui #backend #urgent #refactor #docs #test\n\n---\n\n## Tasks\n"}function createDefaultArchiveContent(){return`${t("markdown.archiveTitle")}\n\n${t("markdown.archiveDesc")}\n\n${t("markdown.archiveSection")}\n\n`}async function backupOriginal(e,t){try{const n=e.replace(/\.md$/,".v1-backup.md");try{return await directoryHandle.getFileHandle(n),!0}catch(e){}const a=await directoryHandle.getFileHandle(n,{create:!0}),o=await a.createWritable();return await o.write(t),await o.close(),console.log("Backup written:",n),!0}catch(e){return console.warn("Backup failed — migration aborted to keep the original intact:",e),!1}}function legacySlug(e){return e.toLowerCase().replace(/[^\w\s-]/g,"").trim().replace(/\s+/g,"-")}function parseMarkdown(e){tasks=[],config={lastTaskId:0,columns:[],categories:[],users:[],priorities:[],tags:[],boardTitle:"Kanban Board"},console.log("=== Starting parseMarkdown ===");const t=e.split("\n",1)[0].replace(/\r$/,"").match(/^#[ \t]+(.+?)[ \t]*$/);t&&(config.boardTitle=t[1].trim());const n=e.match(/<!-- Config: Last Task ID: (\d+) -->/);n&&(config.lastTaskId=parseInt(n[1]),console.log("Last Task ID:",config.lastTaskId));const a=e.match(/## ⚙️ Configuration\s+([\s\S]*?)(?:\n---|\n##\s|$)/);if(a){const e=a[1];console.log("Config section found");const t=e.match(/\*\*Columns\*\*:\s*(.+)/);if(t){console.log("Raw columns:",t[1]);const e=new Set;config.columns=t[1].split("|").map((t,n)=>{if(!(t=t.trim()))return null;const a=t.match(/^(.+?)\s*\((.+?)\)\s*$/);let o,r;a?(o=a[1].trim(),r=a[2].trim()):(o=t,r=legacySlug(t)||"col"+(n+1));let s=r,i=2;for(;e.has(s);)s=`${r}-${i++}`;return e.add(s),{name:o,id:s}}).filter(Boolean),console.log("Parsed columns:",config.columns)}const n=e.match(/\*\*Categories\*\*:\s*(.+)/);n&&(config.categories=n[1].split(",").map(e=>e.trim()).filter(Boolean),console.log("Parsed categories:",config.categories));const o=e.match(/\*\*Users\*\*:\s*(.+)/);o&&(config.users=o[1].split(",").map(e=>e.trim()).filter(Boolean),console.log("Parsed users:",config.users));const r=e.match(/\*\*Priorities\*\*:\s*(.+)/);r&&(config.priorities=r[1].split("|").map(e=>e.trim()).filter(Boolean),console.log("Parsed priorities:",config.priorities));const s=e.match(/\*\*Tags\*\*:\s*(.+)/);s&&(config.tags=s[1].split(/\s+/).filter(e=>e.startsWith("#")).map(e=>e.replace("#","")),console.log("Parsed tags:",config.tags))}0===config.columns.length&&(config.columns=[{name:"📝 To Do",id:"todo"},{name:"🚀 In Progress",id:"in-progress"},{name:"👀 In Review",id:"in-review"},{name:"✅ Done",id:"done"}],console.log("Using default columns")),0===config.categories.length&&(config.categories=["Frontend","Backend","Design","DevOps","Tests","Documentation"]),0===config.users.length&&(config.users=["@user (User)"]),0===config.priorities.length&&(config.priorities=["🔴 Critical","🟠 High","🟡 Medium","🟢 Low"]),0===config.tags.length&&(config.tags=["bug","feature","ui","backend","urgent","refactor","docs","test"]);const o=/<!--\s*Format:\s*v2\s*-->/i.test(e),r=/(^|\n)##\s+Tasks\s*(\n|$)/.test(e),s=[...e.matchAll(/(?:^|\n)##\s+([^\r\n]+?)\s*(?:\n|$)/g)].map(e=>e[1].trim()),i=config.columns.some(t=>{return new RegExp(`(^|\\n)##\\s+${n=t.name,n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\s*(\\n|$)`).test(e)||!r&&s.some(e=>legacySlug(e)===t.id);var n}),l=o||r&&!i;if(loadedFormatV2=l,l){const t=e.replace(/^[\s\S]*?(?:^|\n)##\s+Tasks\s*(?:\n|$)/,"");tasks.push(...parseTaskBlocks(t,config.columns[0].id,!0))}else config.columns.forEach(t=>{tasks.push(...parseTasksFromSection(e,t.name,t.id,!1))});console.log(`\n=== Total tasks parsed: ${tasks.length} ===`),console.log("Tasks:",tasks.map(e=>`${e.id} (${e.status})`).join(", "))}function parseTasksFromSection(e,t,n,a=!1){const o=e.split(/\n##\s+/);for(let e of o)if(e.startsWith(t))return parseTaskBlocks(e.substring(t.length).trim(),n,a);for(let e of o){const t=e.search(/\r?\n/);if(legacySlug((t>=0?e.slice(0,t):e).trim())===n)return parseTaskBlocks(t>=0?e.slice(t).trim():"",n,a)}return[]}function parseTaskBlocks(e,t,n=!1){const a=[],o=[...e.matchAll(/^###\s+TASK-(\d+)\s*\|\s*(.+?)\s*$/gm)];return o.forEach((r,s)=>{const i=r[2].trim();if(!i)return;const l=r.index+r[0].length,c=s+1<o.length?o[s+1].index:e.length,d=e.slice(l,c),m=parseTask("TASK-"+r[1].padStart(3,"0"),i,d,t,n);m&&a.push(m)}),a}function parseTask(e,t,n,a,o=!1){let r=a;if(o){const e=n.match(/^\s*\*\*Status\*\*:\s*([^\r\n]+)/im);if(e){const t=e[1].trim();config.columns.some(e=>e.id===t)&&(r=t)}}const s={id:e,title:t.trim(),status:r,priority:"",category:"",assignees:[],tags:[],created:"",started:"",due:"",completed:"",description:"",subtasks:[],notes:"",extra:""},i=n.match(/\*\*Priority\*\*:\s*(\w+)\s*\|\s*\*\*Category\*\*:\s*([^|]+?)(?:\s*\|\s*\*\*Assigned\*\*:\s*(.+?))?$/m);i&&(s.priority=i[1].trim(),s.category=i[2].trim(),i[3]&&(s.assignees=i[3].split(",").map(e=>e.trim())));const l=n.match(/\*\*Created\*\*:\s*([\d-]+)/);l&&(s.created=l[1]);const c=n.match(/\*\*Started\*\*:\s*([\d-]+)/);c&&(s.started=c[1]);const d=n.match(/\*\*Due\*\*:\s*([\d-]+)/);d&&(s.due=d[1]);const m=n.match(/\*\*Finished\*\*:\s*([\d-]+)/);m&&(s.completed=m[1]);const u=n.match(/\*\*Tags\*\*:\s*(.+)/);u&&(s.tags=u[1].match(/#[\w-]+/g)||[]);const g=n.split("\n");let p=[],k=!1;for(let e of g)if(!e.match(/^\*\*(Status|Priority|Category|Assigned|Created|Started|Due|Finished|Tags)\*\*/)){if(e.match(/^\*\*(Subtasks|Notes|Links|Review|Dependencies)\*\*/))break;e.trim()&&!k&&(k=!0),k&&e.trim()&&p.push(e.trim())}s.description=p.join("\n");let f=!1;for(let e of g)if(/^\s*\*\*Subtasks\*\*/i.test(e))f=!0;else if(f&&/^\s*\*\*[^*\r\n]+\*\*\s*:/.test(e)&&(f=!1),f){const t=e.match(/- \[(x| )\] (.+)/);t&&s.subtasks.push({completed:"x"===t[1],text:t[2].trim()})}const h=n.match(/\*\*Notes\*\*:\s*\n([\s\S]*?)$/);h&&(s.notes=h[1].trim());const y=/^\s*\*\*(Links|Review|Dependencies)\*\*\s*:/i,v=/^\s*\*\*(Status|Priority|Category|Assigned|Created|Started|Due|Finished|Tags|Subtasks)\*\*/i,b=[];let w=!1;for(let e of g){if(/^\s*\*\*Notes\*\*/i.test(e))break;y.test(e)?(w=!0,b.push(e)):(w&&v.test(e)&&(w=!1),w&&b.push(e))}return s.extra=b.join("\n").trim(),s}function escapeHtml(e){return String(e??"").replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[e]))}function markdownToHtml(e){if(!e)return"";let t=e;const n=[];t=t.replace(/```([^\n`]*)\n?([\s\S]*?)```/g,(e,t,a)=>{const o=(t||"").trim()||"text",r=`\n__CODE_BLOCK_${n.length}__\n`,s=`<div style="margin: 1rem 0;"><div style="background: #1a1a1a; color: #888; padding: 0.25rem 0.5rem; border-radius: 6px 6px 0 0; font-size: 0.75rem; font-family: 'Consolas', 'Monaco', monospace;">${o}</div><pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>${a.replace(/\n$/,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}</code></pre></div>`;return n.push(s),r}),t=t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),t=t.replace(/\*\*([^*]+)\*\*:/g,'<strong style="color: var(--primary); display: block; margin-top: 1rem; margin-bottom: 0.5rem;">$1:</strong>'),t=t.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),t=t.replace(/\*([^*]+)\*/g,"<em>$1</em>"),t=t.replace(/`([^`]+)`/g,"<code style=\"background: #2d2d2d; color: #f8f8f2; padding: 0.125rem 0.35rem; border-radius: 3px; font-family: 'Consolas', 'Monaco', monospace; font-size: 0.9em;\">$1</code>"),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>{const a=n.trim();return`<a href="${escapeHtml(/^(https?:|mailto:|#|\/)/i.test(a)?a:"#")}" target="_blank" style="color: var(--primary); text-decoration: underline;">${t}</a>`});const a=t.split("\n");let o=!1,r=[];for(let e of a)""===e.trim()||e.trim().match(/^__CODE_BLOCK_\d+__$/)?(o&&(r.push("</ul>"),o=!1),e.trim().startsWith("__CODE_BLOCK_")?r.push(e.trim()):""===e.trim()&&(o||r.push(""))):e.trim().startsWith("- ")?(o||(r.push('<ul style="margin: 0.5rem 0; padding-left: 1.5rem;">'),o=!0),r.push("<li>"+e.trim().substring(2)+"</li>")):(o&&(r.push("</ul>"),o=!1),e.trim()&&r.push('<p style="margin: 0.5rem 0;">'+e+"</p>"));return o&&r.push("</ul>"),t=r.join("\n"),n.forEach((e,n)=>{t=t.replace(`__CODE_BLOCK_${n}__`,e)}),t}async function autoSave(){if(!kanbanFileHandle)return!1;try{const e=generateMarkdown(),t=await kanbanFileHandle.createWritable();return await t.write(e),await t.close(),currentKanbanContent=e,console.log("Auto-saved"),!0}catch(e){return console.error("Auto-save failed:",e),!1}}function normalizeUserId(e){if(!e)return"";const t=e.match(/^(@[^\s(]+)/);return t?t[1]:e}function getUserFullFormat(e){return extractUniqueValues().users.find(t=>normalizeUserId(t)===e)||e}function extractUniqueValues(){const e=new Set(config.categories||[]),t=new Map,n=new Set(config.tags||[]);return(config.users||[]).forEach(e=>{const n=normalizeUserId(e);t.has(n)||t.set(n,e)}),tasks.forEach(a=>{a.category&&e.add(a.category),a.assignees.forEach(e=>{const n=normalizeUserId(e);t.has(n)||t.set(n,e)}),a.tags.forEach(e=>n.add(e.replace("#","")))}),archivedTasks.forEach(a=>{a.category&&e.add(a.category),a.assignees.forEach(e=>{const n=normalizeUserId(e);t.has(n)||t.set(n,e)}),a.tags.forEach(e=>n.add(e.replace("#","")))}),{categories:[...e],users:[...t.values()],tags:[...n]}}function updateAutocomplete(){const{categories:e,users:n,tags:a}=extractUniqueValues();document.getElementById("categoriesList").innerHTML=e.map(e=>`<option value="${escapeHtml(e)}">`).join(""),document.getElementById("usersList").innerHTML=n.map(e=>`<option value="${escapeHtml(e)}">`).join(""),document.getElementById("tagsList").innerHTML=a.map(e=>`<option value="${escapeHtml(e)}">`).join(""),document.getElementById("filterTagSelect").innerHTML=`<option value="">${t("filters.select")}</option>`+a.map(e=>`<option value="${escapeHtml(e)}">${escapeHtml(e)}</option>`).join(""),document.getElementById("filterCategorySelect").innerHTML=`<option value="">${t("filters.select")}</option>`+e.map(e=>`<option value="${escapeHtml(e)}">${escapeHtml(e)}</option>`).join(""),document.getElementById("filterUserSelect").innerHTML=`<option value="">${t("filters.select")}</option>`+n.map(e=>`<option value="${escapeHtml(normalizeUserId(e))}">${escapeHtml(e)}</option>`).join("");const o=config.priorities||[];document.getElementById("filterPrioritySelect").innerHTML=`<option value="">${t("filters.select")}</option>`+o.map((e,t)=>`<option value="${clean(e)}">${displayPriority(e)}</option>`).join("")}function addFilter(e){let t;"tag"===e?(t=document.getElementById("filterTagSelect").value,document.getElementById("filterTagSelect").value=""):"category"===e?(t=document.getElementById("filterCategorySelect").value,document.getElementById("filterCategorySelect").value=""):"user"===e?(t=document.getElementById("filterUserSelect").value,document.getElementById("filterUserSelect").value=""):"priority"===e&&(t=document.getElementById("filterPrioritySelect").value,document.getElementById("filterPrioritySelect").value=""),t&&!activeFilters.find(n=>n.type===e&&n.value===t)&&(activeFilters.push({type:e,value:t}),renderFilters(),renderKanban())}function removeFilter(e){activeFilters.splice(e,1),renderFilters(),renderKanban()}function clearFilters(){activeFilters=[],renderFilters(),renderKanban()}function applyGlobalSearch(){const e=document.getElementById("globalSearchInput"),t=document.getElementById("clearGlobalSearch");globalSearchTerm=e.value.trim(),t.style.display=globalSearchTerm?"block":"none",renderKanban()}function clearGlobalSearch(){const e=document.getElementById("globalSearchInput"),t=document.getElementById("clearGlobalSearch");e.value="",globalSearchTerm="",t.style.display="none",renderKanban()}function renderFilters(){const e=document.getElementById("activeFilters"),t={tag:"#3b82f6",category:"#8b5cf6",user:"#10b981",priority:"#f59e0b"};e.innerHTML=activeFilters.map((e,n)=>{e.value.replace(/'/g,"\\'");let a=e.value;if("priority"===e.type&&config.priorities){const t=config.priorities.find(t=>clean(t)===e.value);t&&(a=t)}if("user"===e.type){a=getUserFullFormat(e.value)}return`\n <span style="background: ${t[e.type]}; color: white; padding: 0.35rem 0.75rem; border-radius: 16px; font-size: 0.85rem; display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 500;">\n ${displayPriority(a)}\n <button onclick="removeFilter(${n})" style="background: none; border: none; color: white; cursor: pointer; padding: 0; font-weight: bold; font-size: 1.1rem; line-height: 1;" title="Supprimer ce filtre">✕</button>\n </span>\n `}).join("")}function matchesFilters(e){if(activeFilters.length>0){if(!activeFilters.every(t=>"tag"===t.type?e.tags.includes(t.value)||e.tags.includes("#"+t.value):"category"===t.type?e.category===t.value:"user"===t.type?e.assignees.some(e=>normalizeUserId(e)===t.value):"priority"===t.type&&e.priority===t.value))return!1}if(globalSearchTerm){const t=globalSearchTerm.toLowerCase();if(!(e.title&&e.title.toLowerCase().includes(t)||e.description&&e.description.toLowerCase().includes(t)||e.notes&&e.notes.toLowerCase().includes(t)))return!1}return!0}function clickToFilter(e,t){activeFilters.find(n=>n.type===e&&n.value===t)||(activeFilters.push({type:e,value:t}),renderFilters(),renderKanban())}function openColumnsModal(){const e=document.getElementById("columnsModal");document.getElementById("columnsList").innerHTML=config.columns.map((e,n)=>`\n <div style="display: flex; gap: 0.5rem; margin-bottom: 0.75rem; padding: 0.75rem; background: var(--bg); border: 2px solid var(--border, #cbd5e0); border-radius: 6px; align-items: center;">\n <div style="display: flex; gap: 0.25rem;">\n <button onclick="moveColumn(${n}, -1)" ${0===n?"disabled":""} class="btn btn-secondary" style="padding: 0.25rem 0.5rem; font-size: 0.85rem;" title="${t("action.moveUp")}">↑</button>\n <button onclick="moveColumn(${n}, 1)" ${n===config.columns.length-1?"disabled":""} class="btn btn-secondary" style="padding: 0.25rem 0.5rem; font-size: 0.85rem;" title="${t("action.moveDown")}">↓</button>\n </div>\n <input type="text" value="${escapeHtml(e.name)}" onchange="updateColumn(${n}, 'name', this.value)" style="flex: 1; padding: 0.5rem; border: 1px solid var(--border, #cbd5e0); border-radius: 4px; background: var(--bg); color: var(--text);">\n <input type="text" value="${escapeHtml(e.id)}" onchange="updateColumn(${n}, 'id', this.value)" style="width: 120px; padding: 0.5rem; border: 1px solid var(--border, #cbd5e0); border-radius: 4px; background: var(--bg); color: var(--text);" placeholder="ID">\n <button onclick="deleteColumn(${n})" class="btn btn-secondary" style="padding: 0.5rem;">🗑️</button>\n </div>\n `).join(""),e.classList.add("active")}function closeColumnsModal(){document.getElementById("columnsModal").classList.remove("active")}function addColumn(){const e=prompt(t("prompt.columnName")),n=prompt(t("prompt.columnId"));e&&n&&(config.columns.push({name:e,id:n}),openColumnsModal(),autoSave(),renderKanban())}function updateColumn(e,t,n){config.columns[e][t]=n,autoSave(),renderKanban()}function deleteColumn(e){confirm(t("confirm.deleteColumn"))&&(config.columns.splice(e,1),openColumnsModal(),autoSave(),renderKanban())}function moveColumn(e,t){const n=e+t;n<0||n>=config.columns.length||([config.columns[e],config.columns[n]]=[config.columns[n],config.columns[e]],openColumnsModal(),autoSave(),renderKanban())}function openTaskModal(e=null,n=null){isEditMode=!!e;const a=document.getElementById("newTaskModal"),o=document.getElementById("newTaskForm"),r=a.querySelector("h2"),s=document.getElementById("taskFormSubmitBtn");r.textContent=t(isEditMode?"taskForm.editTask":"taskForm.newTask"),s.textContent=t(isEditMode?"taskForm.save":"taskForm.create"),document.getElementById("taskStatus").innerHTML=config.columns.map(e=>`<option value="${escapeHtml(e.id)}">${escapeHtml(e.name)}</option>`).join(""),document.getElementById("taskPriority").innerHTML=config.priorities.map(e=>`<option value="${clean(e)}">${displayPriority(e)}</option>`).join(""),updateAutocomplete(),isEditMode?(document.getElementById("taskEditId").value=e.id,document.getElementById("taskTitle").value=e.title,document.getElementById("taskStatus").value=e.status,document.getElementById("taskPriority").value=e.priority||"",document.getElementById("taskCategory").value=e.category||"",document.getElementById("taskAssignee").value=e.assignees.join(", "),document.getElementById("taskCreated").value=e.created||"",document.getElementById("taskStarted").value=e.started||"",document.getElementById("taskDue").value=e.due||"",document.getElementById("taskCompleted").value=e.completed||"",document.getElementById("taskTags").value=e.tags.join(" "),document.getElementById("taskDescription").value=e.description||"",document.getElementById("taskNotes").value=e.notes||"",formSubtasks=JSON.parse(JSON.stringify(e.subtasks||[]))):(o.reset(),document.getElementById("taskEditId").value="",n&&(document.getElementById("taskStatus").value=n),formSubtasks=[]),renderFormSubtasks(),a.classList.add("active")}function closeTaskModal(){document.getElementById("newTaskModal").classList.remove("active"),document.getElementById("newTaskForm").reset(),formSubtasks=[],isEditMode=!1}function renderFormSubtasks(){document.getElementById("formSubtasksList").innerHTML=formSubtasks.map((e,t)=>`\n <li style="padding: 0.5rem; margin-bottom: 0.25rem; background: var(--bg); border: 1px solid var(--border, #cbd5e0); border-radius: 4px; display: flex; align-items: center; gap: 0.5rem;">\n <input type="checkbox" ${e.completed?"checked":""} onchange="toggleFormSubtask(${t})" style="width: 16px; height: 16px; cursor: pointer;">\n <span style="flex: 1; ${e.completed?"text-decoration: line-through; color: #999;":""}">${e.text.replace(/</g,"<").replace(/>/g,">")}</span>\n <button type="button" onclick="deleteFormSubtask(${t})" style="background: none; border: none; cursor: pointer; color: #e53e3e; font-size: 1rem;">🗑️</button>\n </li>\n `).join("")}function addFormSubtask(){const e=document.getElementById("formSubtaskInput"),t=e.value.trim();t&&(formSubtasks.push({completed:!1,text:t}),e.value="",renderFormSubtasks())}function toggleFormSubtask(e){formSubtasks[e]&&(formSubtasks[e].completed=!formSubtasks[e].completed,renderFormSubtasks())}function deleteFormSubtask(e){formSubtasks.splice(e,1),renderFormSubtasks()}function renderKanban(){const e=document.getElementById("kanbanBoard");if(e.innerHTML="",console.log("Rendering kanban with",tasks.length,"tasks"),!config.columns||0===config.columns.length)return;document.getElementById("debugInfo").textContent=`Loaded ${tasks.length} tasks\nColumns: ${config.columns.map(e=>e.name).join(", ")}`,config.columns.forEach(n=>{const a=tasks.filter(e=>e.status===n.id&&matchesFilters(e));console.log(`Column ${n.id}: ${a.length} tasks`);const o=document.createElement("div");o.className="kanban-column",o.dataset.columnId=n.id,o.innerHTML=`\n <div class="column-header">\n <div class="column-title">\n ${escapeHtml(n.name)}\n </div>\n <div style="display: flex; align-items: center; gap: 0.5rem;">\n <div class="column-count">${a.length}</div>\n <button class="column-add-btn">+</button>\n </div>\n </div>\n <div class="task-list" ondrop="drop(event)" ondragover="allowDrop(event)">\n ${0===a.length?`<div class="empty-state">${t("empty.noTasks")}</div>`:""}\n </div>\n `;const r=o.querySelector(".task-list"),s=o.querySelector(".column-add-btn");s.title=t("taskForm.newTask"),s.addEventListener("click",()=>openTaskModal(null,n.id)),a.forEach(e=>{const t=createTaskElement(e);r.appendChild(t)}),e.appendChild(o)})}function dueStatus(e){if(!e.due||e.completed)return null;const t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e.due.trim());if(!t)return null;const n=new Date(+t[1],+t[2]-1,+t[3]);if(n.getFullYear()!==+t[1]||n.getMonth()!==+t[2]-1||n.getDate()!==+t[3])return null;const a=new Date;a.setHours(0,0,0,0);const o=Math.round((n-a)/864e5);return o<0?"overdue":o<=2?"soon":null}function createTaskElement(e){const n=document.createElement("div");n.className="task-card",n.draggable=!0,n.dataset.taskId=e.id;const a=dueStatus(e);a&&n.classList.add(`task-card--${a}`);var o="Default";if(config.priorities){const t=config.priorities.find(t=>clean(t)===e.priority);if(t){const e=getFirstEmoji(t);e&&priorityIconClasses[e]&&(o=priorityIconClasses[e])}}const r=e.subtasks.length>0?`<div class="task-subtasks">\n <div class="subtask-progress">\n <div class="progress-bar">\n <div class="progress-fill" style="width: ${e.subtasks.filter(e=>e.completed).length/e.subtasks.length*100}%"></div>\n </div>\n <span>${e.subtasks.filter(e=>e.completed).length}/${e.subtasks.length}</span>\n </div>\n </div>`:"";n.innerHTML=`\n <div class="task-header">\n <span class="task-id">${escapeHtml(e.id)}</span>\n <button class="task-edit-btn" data-edit-task="1" style="background: none; border: none; cursor: pointer; font-size: 1.1rem; padding: 0.25rem;">✏️</button>\n </div>\n <div class="task-title">${escapeHtml(e.title)}</div>\n ${e.description?`<div class="task-description">${markdownToHtml(e.description)}</div>`:""}\n <div class="task-meta">\n ${e.due?`<span class="badge badge-due${a?` badge-due--${a}`:""}" title="${escapeHtml(t("overdue"===a?"tooltip.overdue":"soon"===a?"tooltip.dueSoon":"meta.due"))}">${"overdue"===a?"⚠️":"📅"} ${escapeHtml(e.due)}</span>`:""}\n ${e.priority?`<span class="badge badge-priority ${o}" data-filter="priority" data-filter-value="${escapeHtml(e.priority)}" style="cursor: pointer;" title="${t("tooltip.filterByPriority")}">${escapeHtml(displayPriority(e.priority))}</span>`:""}\n ${e.category?`<span class="badge badge-category" data-filter="category" data-filter-value="${escapeHtml(e.category)}" style="cursor: pointer;" title="${t("tooltip.filterByCategory")}">${escapeHtml(e.category)}</span>`:""}\n ${e.assignees.map(e=>`<span class="badge badge-assignee" data-filter="user" data-filter-value="${escapeHtml(normalizeUserId(e))}" style="cursor: pointer;" title="${t("tooltip.filterByUser")}">${escapeHtml(e)}</span>`).join("")}\n ${e.tags.map(e=>`<span class="tag" data-filter="tag" data-filter-value="${escapeHtml(e)}" style="cursor: pointer;" title="${t("tooltip.filterByTag")}">${escapeHtml(e)}</span>`).join("")}\n </div>\n ${r}\n `;const s=n.querySelector("[data-edit-task]");return s&&s.addEventListener("click",t=>{t.stopPropagation(),openTaskModal(e)}),n.querySelectorAll("[data-filter]").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation(),clickToFilter(e.dataset.filter,e.dataset.filterValue)})}),n.addEventListener("dragstart",drag),n.addEventListener("click",()=>showTaskDetail(e)),attachTouchDrag(n),n}function allowDrop(e){e.preventDefault()}function drag(e){e.dataTransfer.setData("taskId",e.target.dataset.taskId),e.target.classList.add("dragging")}function getDropBeforeId(e,t,n=null){const a=[...e.querySelectorAll(".task-card:not(.dragging)")];for(const e of a){if(e.dataset.taskId===n)continue;const a=e.getBoundingClientRect();if(t<a.top+a.height/2)return e.dataset.taskId}return null}function moveTask(e,t,n=null){const a=tasks.findIndex(t=>t.id===e);if(a<0)return!1;const o=tasks[a],r=t&&o.status!==t,s=t||o.status;let i;if(tasks.splice(a,1),n)i=tasks.findIndex(e=>e.id===n),i<0&&(i=tasks.length);else{i=tasks.length;for(let e=tasks.length-1;e>=0;e--)if(tasks[e].status===s){i=e+1;break}}tasks.splice(i,0,o);const l=tasks.indexOf(o)!==a;return t&&(o.status=t),r||l}function drop(e){e.preventDefault();const n=e.dataTransfer.getData("taskId");let a=e.target;for(;a&&!a.classList.contains("task-list");)a=a.parentElement;if(!a||!a.classList.contains("task-list"))return;const o=a.closest(".kanban-column").dataset.columnId,r=getDropBeforeId(a,e.clientY,n),s=document.querySelector(`[data-task-id="${n}"]`);s&&s.classList.remove("dragging"),moveTask(n,o,r)&&(renderKanban(),autoSave(),showNotification(t("notif.taskMoved"),"success"))}window.addEventListener("DOMContentLoaded",async()=>{await tryRestorePreviousDirectory(),document.getElementById("projectSelector").addEventListener("change",async e=>{const t=parseInt(e.target.value);isNaN(t)||await switchProject(t)})}),document.getElementById("selectFolderBtn").addEventListener("click",async()=>{try{const e={};directoryHandle&&(e.startIn=directoryHandle),directoryHandle=await window.showDirectoryPicker(e),await saveDirectoryHandle(directoryHandle),await loadKanbanFile(),document.getElementById("welcomeScreen").style.display="none",document.getElementById("kanbanView").style.display="block",document.getElementById("filterBar").style.display="block",document.getElementById("newTaskBtn").style.display="inline-flex",document.getElementById("archiveBtn").style.display="inline-flex",document.getElementById("manageColsBtn").style.display="inline-flex",await updateProjectSelector(),showNotification(t("notif.folderLoaded"),"success")}catch(e){"AbortError"!==e.name&&(showNotification(t("notif.folderError"),"error"),console.error(e))}});let touchDrag=null;function attachTouchDrag(e){e.addEventListener("touchstart",t=>{if(1!==t.touches.length)return;const n=t.touches[0];touchDrag={taskId:e.dataset.taskId,el:e,startX:n.clientX,startY:n.clientY,active:!1,timer:setTimeout(()=>{touchDrag&&(touchDrag.active=!0,e.classList.add("dragging"),navigator.vibrate&&navigator.vibrate(10))},250)}},{passive:!0}),e.addEventListener("touchmove",e=>{if(!touchDrag)return;const t=e.touches[0];if(!touchDrag.active)return void((Math.abs(t.clientX-touchDrag.startX)>10||Math.abs(t.clientY-touchDrag.startY)>10)&&(clearTimeout(touchDrag.timer),touchDrag=null));e.preventDefault();const n=document.elementFromPoint(t.clientX,t.clientY),a=n&&n.closest(".kanban-column");document.querySelectorAll(".kanban-column").forEach(e=>e.classList.toggle("drop-target",e===a))},{passive:!1});const n=e=>{if(touchDrag){if(clearTimeout(touchDrag.timer),touchDrag.active){e.preventDefault(),touchDrag.el.classList.remove("dragging");const n=e.changedTouches[0],a=document.elementFromPoint(n.clientX,n.clientY),o=a&&a.closest(".kanban-column");if(document.querySelectorAll(".kanban-column").forEach(e=>e.classList.remove("drop-target")),o){const e=o.dataset.columnId,a=getDropBeforeId(o.querySelector(".task-list"),n.clientY,touchDrag.taskId);moveTask(touchDrag.taskId,e,a)&&(renderKanban(),autoSave(),showNotification(t("notif.taskMoved"),"success"))}}touchDrag=null}};e.addEventListener("touchend",n,{passive:!1}),e.addEventListener("touchcancel",n,{passive:!1})}function showTaskDetail(e){currentDetailTask=e;const n=document.getElementById("taskModalActions"),a=document.getElementById("taskModal"),o=document.getElementById("modalBody"),r=document.getElementById("archiveModal");let s=`<button class="btn btn-secondary" onclick="closeModal()">${t("taskDetail.close")}</button>`;wasArchiveModalActive=r.classList.contains("active"),wasArchiveModalActive?r.classList.remove("active"):(s+=`<button class="btn btn-primary" onclick="editCurrentTask()">${t("taskDetail.edit")}</button>`,s+=`<button class="btn btn-secondary" onclick="archiveCurrentTask()">${t("taskDetail.archive")}</button>`,s+=`<button class="btn btn-secondary" onclick="deleteCurrentTask()">${t("taskDetail.delete")}</button>`),n.innerHTML=s;let i=e.priority;if(e.priority){const t=config.priorities.find(t=>clean(t)===e.priority);t&&(i=displayPriority(t))}const l=config.columns.find(t=>t.id===e.status),c=l?l.name:e.status;o.innerHTML=`\n <div style="padding: 1.5rem;">\n \x3c!-- Task ID Badge --\x3e\n <div style="display: inline-block; background: var(--primary); color: white; padding: 0.25rem 0.75rem; border-radius: 4px; font-size: 0.85rem; font-weight: 600; margin-bottom: 1rem;">\n ${e.id}\n </div>\n\n \x3c!-- Title --\x3e\n <h3 style="margin: 0 0 1.5rem 0; font-size: 1.5rem; color: var(--text);">${escapeHtml(e.title)}</h3>\n\n \x3c!-- Metadata Grid --\x3e\n <div class="modal-form-grid" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; margin-bottom: 1.5rem; padding: 1rem; background: var(--bg); border-radius: 8px;">\n ${e.priority?`\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.priority")}</div>\n <div style="font-weight: 500;">${displayPriority(i)}</div>\n </div>\n `:""}\n\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.status")}</div>\n <div style="font-weight: 500;">${escapeHtml(c)}</div>\n </div>\n\n ${e.category?`\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.category")}</div>\n <div style="font-weight: 500;">${escapeHtml(e.category)}</div>\n </div>\n `:""}\n\n ${e.assignees.length>0?`\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.assigned")}</div>\n <div style="font-weight: 500;">${escapeHtml(e.assignees.join(", "))}</div>\n </div>\n `:""}\n\n ${e.created?`\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.created")}</div>\n <div style="font-weight: 500;">${e.created}</div>\n </div>\n `:""}\n\n ${e.started?`\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.started")}</div>\n <div style="font-weight: 500;">${e.started}</div>\n </div>\n `:""}\n\n ${e.due?`\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.due")}</div>\n <div style="font-weight: 500;">${e.due}</div>\n </div>\n `:""}\n\n ${e.completed?`\n <div>\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.25rem;">${t("meta.completed")}</div>\n <div style="font-weight: 500;">${e.completed}</div>\n </div>\n `:""}\n </div>\n\n \x3c!-- Tags --\x3e\n ${e.tags.length>0?`\n <div style="margin-bottom: 1.5rem;">\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.5rem;">${t("meta.tags")}</div>\n <div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">\n ${e.tags.map(e=>`\n <span style="background: var(--primary); color: white; padding: 0.25rem 0.75rem; border-radius: 12px; font-size: 0.85rem;">\n ${e}\n </span>\n `).join("")}\n </div>\n </div>\n `:""}\n\n \x3c!-- Description --\x3e\n ${e.description?`\n <div style="margin-bottom: 1.5rem;">\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.5rem; font-weight: 600;">${t("meta.description")}</div>\n <div style="line-height: 1.6; color: var(--text);">${markdownToHtml(e.description)}</div>\n </div>\n `:""}\n\n \x3c!-- Subtasks --\x3e\n <div style="margin-bottom: 1.5rem;">\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.5rem; font-weight: 600;">${t("meta.subtasks",{completed:e.subtasks.filter(e=>e.completed).length,total:e.subtasks.length})}</div>\n <ul id="subtasksList" style="list-style: none; padding: 0; margin: 0 0 1rem 0;">\n ${e.subtasks.map((n,a)=>{const o=n.text.replace(/</g,"<").replace(/>/g,">");return`\n <li style="padding: 0.5rem; margin-bottom: 0.25rem; background: var(--bg); border-radius: 4px; display: flex; align-items: center; gap: 0.5rem;">\n <input type="checkbox" ${n.completed?"checked":""} onchange="toggleSubtask('${e.id}', ${a})" style="width: 18px; height: 18px; cursor: pointer;">\n <span ondblclick="editSubtask('${e.id}', ${a})" style="flex: 1; ${n.completed?"text-decoration: line-through; color: var(--text-secondary);":""} cursor: pointer;" title="${t("tooltip.doubleClickEdit")}">${o}</span>\n <button onclick="deleteSubtask('${e.id}', ${a})" style="background: none; border: none; cursor: pointer; color: #e53e3e; font-size: 1.1rem; padding: 0.25rem;" title="${t("tooltip.delete")}">🗑️</button>\n </li>\n `}).join("")}\n </ul>\n ${wasArchiveModalActive?"":`\n <div style="display: flex; gap: 0.5rem;">\n <input type="text" id="newSubtaskInput" placeholder="${t("subtask.newPlaceholder")}" onkeypress="if(event.key==='Enter') addSubtask('${e.id}')" style="flex: 1; padding: 0.5rem; border: 2px solid var(--border, #cbd5e0); border-radius: 4px; font-size: 0.9rem; background: var(--bg); color: var(--text);">\n <button onclick="addSubtask('${e.id}')" class="btn btn-primary" style="padding: 0.5rem 1rem;">${t("taskForm.subtaskAdd")}</button>\n </div>\n `}\n </div>\n\n \x3c!-- Notes --\x3e\n ${e.notes?`\n <div style="margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid #e2e8f0;">\n <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.75rem; font-weight: 600;">${t("meta.notes")}</div>\n <div style="line-height: 1.7; color: var(--text); background: var(--bg); padding: 1rem; border-radius: 8px; border-left: 4px solid var(--primary);">\n ${markdownToHtml(e.notes)}\n </div>\n </div>\n `:""}\n </div>\n `,a.classList.add("active")}function closeModal(){document.getElementById("taskModal").classList.remove("active"),wasArchiveModalActive&&(document.getElementById("archiveModal").classList.add("active"),wasArchiveModalActive=!1)}function editCurrentTask(){currentDetailTask&&(closeModal(),openTaskModal(currentDetailTask))}function toggleSubtask(e,t){const n=tasks.find(t=>t.id===e);n&&n.subtasks[t]&&(n.subtasks[t].completed=!n.subtasks[t].completed,currentDetailTask=n,showTaskDetail(n),renderKanban(),autoSave())}function deleteSubtask(e,n){const a=tasks.find(t=>t.id===e);a&&confirm(t("confirm.deleteSubtask"))&&(a.subtasks.splice(n,1),currentDetailTask=a,showTaskDetail(a),renderKanban(),autoSave())}function addSubtask(e){const t=document.getElementById("newSubtaskInput"),n=t.value.trim();if(!n)return;const a=tasks.find(t=>t.id===e);a&&(a.subtasks.push({completed:!1,text:n}),currentDetailTask=a,t.value="",showTaskDetail(a),renderKanban(),autoSave())}function editSubtask(e,n){const a=tasks.find(t=>t.id===e);if(!a||!a.subtasks[n])return;const o=prompt(t("prompt.editSubtask"),a.subtasks[n].text);null!==o&&o.trim()&&(a.subtasks[n].text=o.trim(),currentDetailTask=a,showTaskDetail(a),renderKanban(),autoSave())}async function loadArchive(){console.log("🗃️ === LOADING ARCHIVE ===");try{archiveFileHandle=await directoryHandle.getFileHandle("archive.md"),console.log("✓ Archive file handle obtained");const e=await archiveFileHandle.getFile();console.log("✓ Archive file loaded, size:",e.size,"bytes");const t=await e.text();console.log("✓ Archive content read, length:",t.length,"characters"),console.log("Archive content preview:",t.substring(0,500)),loadedArchiveV2=/<!--\s*Format:\s*v2\s*-->/i.test(t),archivedTasks=parseTaskBlocks(t,"archived",!0),console.log("✓ Archive parsed. Total archived tasks:",archivedTasks.length);const n=(t.match(/^###\s+TASK-/gm)||[]).length,a=new Set(archivedTasks.map(e=>e.id)).size;if(!loadedArchiveV2&&archivedTasks.length>0&&archivedTasks.length===n&&a===n)try{await backupOriginal("archive.md",t)?(await saveArchive(),console.log("Migrated archive.md to V2 format")):console.warn("Skipped archive.md migration: backup failed. File left unchanged.")}catch(e){console.error("Archive migration write failed (kept in memory):",e)}else!loadedArchiveV2&&archivedTasks.length>0&&console.warn(`Skipped archive.md migration: incomplete parse (${archivedTasks.length}/${n} blocks). File left unchanged.`)}catch(e){console.error("❌ archive.md not found or error:",e),archivedTasks=[]}}async function saveArchive(){archiveFileHandle||(archiveFileHandle=await directoryHandle.getFileHandle("archive.md",{create:!0}));let e=`${t("markdown.archiveTitle")}\n\x3c!-- Format: v2 --\x3e\n\n${t("markdown.archiveDesc")}\n\n${t("markdown.archiveSection")}\n\n`;archivedTasks.forEach(t=>{e+=`### ${t.id} | ${t.title}\n`,e+=`**Status**: ${t.status}\n`;let n="";t.priority&&(n+=`**Priority**: ${t.priority}`),t.category&&(n+=` | **Category**: ${t.category}`),t.assignees.length>0&&(n+=` | **Assigned**: ${t.assignees.join(", ")}`),n&&(e+=`${n}\n`);let a="";t.created&&(a+=`**Created**: ${t.created}`),t.started&&(a+=(a?" | ":"")+`**Started**: ${t.started}`),t.due&&(a+=(a?" | ":"")+`**Due**: ${t.due}`),t.completed&&(a+=(a?" | ":"")+`**Finished**: ${t.completed}`),a&&(e+=`${a}\n`),t.tags.length>0&&(e+=`**Tags**: ${t.tags.join(" ")}\n`),t.description&&(e+=`\n${t.description}\n`),t.subtasks.length>0&&(e+="\n**Subtasks**:\n",t.subtasks.forEach(t=>e+=`- [${t.completed?"x":" "}] ${t.text}\n`)),t.extra&&(e+=`\n${t.extra}\n`),t.notes&&(e+=`\n**Notes**:\n${t.notes}\n`),e+="\n"});const n=await archiveFileHandle.createWritable();await n.write(e),await n.close()}function archiveCurrentTask(){if(currentDetailTask&&confirm(t("confirm.archiveTask",{title:currentDetailTask.title}))){const e=tasks.findIndex(e=>e.id===currentDetailTask.id);if(e>=0){const n=tasks.splice(e,1)[0];archivedTasks.push(n),saveArchive(),autoSave(),updateAutocomplete(),renderKanban(),closeModal(),showNotification(t("notif.taskArchived"),"success")}}}function deleteCurrentTask(){currentDetailTask&&deleteTask(currentDetailTask.id,!1)}function deleteTask(e,n=!1){const a=n?archivedTasks:tasks,o=a.find(t=>t.id===e);if(!o)return;const r=t(n?"confirm.deleteTask":"confirm.deleteTaskFromArchive",{title:o.title});if(confirm(r)){const o=a.findIndex(t=>t.id===e);o>=0&&(a.splice(o,1),n?saveArchive():autoSave(),n?renderArchiveList(document.getElementById("archiveSearch").value):(renderKanban(),closeModal()),showNotification(t("notif.taskDeleted"),"success"))}}async function openArchiveModal(){console.log("\n📂 === OPENING ARCHIVE MODAL ==="),await loadArchive(),renderArchiveList(),document.getElementById("archiveModal").classList.add("active"),console.log("✓ Archive modal opened")}function closeArchiveModal(){document.getElementById("archiveModal").classList.remove("active")}function renderArchiveList(e=""){console.log("\n🎨 === RENDERING ARCHIVE LIST ==="),console.log("Search term:",e||"(none)"),console.log("Total archived tasks:",archivedTasks.length);const n=document.getElementById("archiveList");if(!n)return void console.error("❌ archiveList element not found!");const a=e?archivedTasks.filter(t=>t.title.toLowerCase().includes(e.toLowerCase())||t.description.toLowerCase().includes(e.toLowerCase())||t.tags.some(t=>t.toLowerCase().includes(e.toLowerCase()))||t.category.toLowerCase().includes(e.toLowerCase())):archivedTasks;if(console.log("Filtered tasks:",a.length),0===a.length)return console.log("⚠️ No tasks to display - showing empty message"),void(n.innerHTML=`<p style="text-align: center; color: var(--text-secondary, #999); padding: 2rem;">${t("archives.empty")}</p>`);console.log("✓ Rendering",a.length,"tasks"),n.innerHTML=a.map(e=>`\n <div class="task-card" onclick="showTaskDetail(archivedTasks.find(t => t.id === '${e.id}'))">\n <div style="background: var(--bg); border: 2px solid var(--border, #e2e8f0); border-radius: 8px; padding: 1rem; margin-bottom: 0.75rem;">\n <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.5rem;">\n <div>\n <span style="background: #6b7280; color: white; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600;">${e.id}</span>\n <strong style="margin-left: 0.5rem; font-size: 1.1rem;">${escapeHtml(e.title)}</strong>\n </div>\n <div style="display: flex; gap: 0.5rem;">\n <button onclick="event.stopPropagation(); deleteTask('${e.id}', true)" class="btn btn-secondary" style="padding: 0.4rem 0.8rem; font-size: 0.85rem; background: #ef4444; color: white;">🗑️</button>\n <button onclick="event.stopPropagation(); unarchiveTask('${e.id}')" class="btn btn-primary" style="padding: 0.4rem 0.8rem; font-size: 0.85rem;">${t("action.restore")}</button>\n </div>\n </div>\n ${e.description?`<p style="color: var(--text-secondary, #666); margin: 0.5rem 0;">${markdownToHtml(e.description)}</p>`:""}\n <div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">\n ${e.priority?`<span style="background: #fbbf24; color: white; padding: 0.25rem 0.5rem; border-radius: 12px; font-size: 0.8rem;">${displayPriority(e.priority)}</span>`:""}\n ${e.category?`<span style="background: #8b5cf6; color: white; padding: 0.25rem 0.5rem; border-radius: 12px; font-size: 0.8rem;">${escapeHtml(e.category)}</span>`:""}\n ${e.tags.map(e=>`<span style="background: #3b82f6; color: white; padding: 0.25rem 0.5rem; border-radius: 12px; font-size: 0.8rem;">${escapeHtml(e)}</span>`).join("")}\n </div>\n </div>\n `).join("")}function unarchiveTask(e){const n=archivedTasks.findIndex(t=>t.id===e);if(n>=0){const e=archivedTasks.splice(n,1)[0];config.columns.find(t=>t.id===e.status)||(e.status=config.columns[0]?.id||"todo"),tasks.push(e),saveArchive(),autoSave(),updateAutocomplete(),renderKanban(),renderArchiveList(document.getElementById("archiveSearch").value),showNotification(t("notif.taskRestored"),"success")}}function generateMarkdown(){let e=`# ${config.boardTitle&&config.boardTitle.trim()||"Kanban Board"}\n\n\x3c!-- Config: Last Task ID: ${config.lastTaskId} --\x3e\n\x3c!-- Format: v2 --\x3e\n\n`;return config.categories=[...new Set(config.categories||[])],config.users=[...new Set(config.users||[])],config.tags=[...new Set(config.tags||[])],0===config.categories.length&&(config.categories=["Frontend","Backend","Design","DevOps","Tests","Documentation"]),0===config.users.length&&(config.users=["@user (User)"]),0===config.priorities.length&&(config.priorities=["🔴 Critical","🟠 High","🟡 Medium","🟢 Low"]),0===config.tags.length&&(config.tags=["bug","feature","ui","backend","urgent","refactor","docs","test"]),e+="## ⚙️ Configuration\n\n",e+=`**Columns**: ${config.columns.map(e=>`${e.name} (${e.id})`).join(" | ")}\n\n`,e+=`**Categories**: ${config.categories.join(", ")}\n\n`,e+=`**Users**: ${config.users.join(", ")}\n\n`,e+=`**Priorities**: ${config.priorities.join(" | ")}\n\n`,e+=`**Tags**: ${config.tags.map(e=>"#"+e).join(" ")}\n\n`,e+="---\n\n",e+="## Tasks\n\n",tasks.forEach(t=>{e+=`### ${t.id} | ${t.title}\n`,e+=`**Status**: ${t.status}\n`;let n="";t.priority&&(n+=`**Priority**: ${t.priority}`),t.category&&(n+=` | **Category**: ${t.category}`),t.assignees.length>0&&(n+=` | **Assigned**: ${t.assignees.join(", ")}`),n&&(e+=n+"\n");let a="";t.created&&(a+=`**Created**: ${t.created}`),t.started&&(a+=(a?" | ":"")+`**Started**: ${t.started}`),t.due&&(a+=(a?" | ":"")+`**Due**: ${t.due}`),t.completed&&(a+=(a?" | ":"")+`**Finished**: ${t.completed}`),a&&(e+=a+"\n"),t.tags.length>0&&(e+=`**Tags**: ${t.tags.join(" ")}\n`),t.description&&(e+=`\n${t.description}\n`),t.subtasks.length>0&&(e+="\n**Subtasks**:\n",t.subtasks.forEach(t=>{e+=`- [${t.completed?"x":" "}] ${t.text}\n`})),t.extra&&(e+=`\n${t.extra}\n`),t.notes&&(e+=`\n**Notes**:\n${t.notes}\n`),e+="\n"}),e}function showNotification(e,t="success"){const n=document.getElementById("notification");document.getElementById("notificationText").textContent=e,n.className=`notification ${t} show`,setTimeout(()=>{n.classList.remove("show")},3e3)}document.getElementById("newTaskForm").addEventListener("submit",async e=>{e.preventDefault();const n=document.getElementById("taskTitle").value.trim(),a=document.getElementById("taskStatus").value,o=document.getElementById("taskPriority").value,r=document.getElementById("taskCategory").value.trim(),s=document.getElementById("taskAssignee").value.trim(),i=document.getElementById("taskCreated").value,l=document.getElementById("taskStarted").value,c=document.getElementById("taskDue").value,d=document.getElementById("taskCompleted").value,m=document.getElementById("taskTags").value.trim(),u=document.getElementById("taskDescription").value.trim(),g=document.getElementById("taskNotes").value.trim(),p=m?m.split(/\s+/).filter(e=>e.startsWith("#")||e).map(e=>e.startsWith("#")?e:"#"+e):[],k=s?s.split(",").map(e=>e.trim()):[];if(isEditMode){const e=document.getElementById("taskEditId").value,s=tasks.find(t=>t.id===e);s&&(s.title=n,s.status=a,s.priority=o,s.category=r,s.assignees=k,s.tags=p,s.created=i,s.started=l,s.due=c,s.completed=d,s.description=u,s.subtasks=formSubtasks,s.notes=g,showNotification(t("notif.taskEdited",{id:e}),"success"))}else{config.lastTaskId++;const e="TASK-"+String(config.lastTaskId).padStart(3,"0");tasks.push({id:e,title:n,status:a,priority:o,category:r,assignees:k,tags:p,created:i||(new Date).toISOString().split("T")[0],started:l||"",due:c,completed:d||"",description:u,subtasks:formSubtasks,notes:g}),showNotification(t("notif.taskCreated",{id:e}),"success")}closeTaskModal(),renderKanban(),autoSave()}),document.getElementById("archiveSearch").addEventListener("input",e=>{renderArchiveList(e.target.value)}),document.getElementById("newTaskBtn").addEventListener("click",()=>openTaskModal()),document.getElementById("archiveBtn").addEventListener("click",openArchiveModal),document.getElementById("manageColsBtn").addEventListener("click",openColumnsModal)</script></body></html>