@ashikahmad/kanbanmd 0.0.0-stage → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/kanbanmd.html ADDED
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Kanban.md</title><link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cstyle%3E.t%7Bfill:%232F5BEA%7D.h%7Bstroke:%23C9D6FF%7D.a%7Bfill:%23fff%7D.b%7Bfill:%239BB3FF%7D@media(prefers-color-scheme:dark)%7B.t%7Bfill:%231B1F26;stroke:%23343B47;stroke-width:1.5px%7D.h%7Bstroke:%237B9BFF%7D.a%7Bfill:%23E8EAEE%7D.b%7Bfill:%237B9BFF%7D%7D%3C/style%3E%3Crect class='t' x='.75' y='.75' width='62.5' height='62.5' rx='14.5'/%3E%3Cpath class='h' d='M15.4 12.8 14.4 26.4M20 12.8 19 26.4M12 17.2h10.8M11.6 22.4h10.8' stroke-width='2.4' stroke-linecap='round' fill='none'/%3E%3Crect class='a' x='26.5' y='12' width='11' height='40' rx='3'/%3E%3Crect class='b' x='42' y='12' width='11' height='28' rx='3'/%3E%3C/svg%3E"><style>*{margin:0;padding:0;box-sizing:border-box}:root{--s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-5:24px;--s-6:32px;--r-sm:6px;--r-md:10px;--fs-xs:11px;--fs-sm:12.5px;--fs-md:14px;--fs-lg:16px;--fs-xl:20px;--font:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;--mono:ui-monospace,'SF Mono',Menlo,Consolas,monospace;--control-h:32px;--col-min:280px;--col-max:460px;--rail-w:48px;--rail-open-w:220px;--c-page:#E9ECF1;--c-column:#F4F6F9;--c-card:#FFFFFF;--c-input:#FFFFFF;--c-pop:#FFFFFF;--c-hover:rgba(15, 23, 42, 0.05);--c-fill:rgba(100, 116, 139, 0.13);--c-border:#E1E5EB;--c-border-strong:#CDD3DC;--c-text:#171A1F;--c-muted:#5F6875;--c-faint:#8B93A1;--c-accent:#2F5BEA;--c-accent-hover:#2349C4;--c-accent-soft:rgba(47, 91, 234, 0.10);--c-on-accent:#FFFFFF;--c-danger:#D23B3B;--c-warn:#B7791F;--c-ok:#3FA66A;--c-overlay:rgba(15, 23, 42, 0.40);--shadow-pop:0 10px 30px rgba(15, 23, 42, 0.10),0 0 0 1px rgba(15, 23, 42, 0.06);--c-code-bg:var(--c-fill);--c-code-text:#171A1F;--logo-tile:#2F5BEA;--logo-ring:transparent;--logo-hash:#C9D6FF;--logo-tall:#FFFFFF;--logo-short:#9BB3FF;--priority-default:#8B93A1;--priority-red:#D93A40;--priority-orange:#E8590C;--priority-yellow:#A87D00;--priority-green:#2B8A57;--priority-blue:#3B82F6;--priority-purple:#8B6BD8;--priority-white:#CFD4DC;--priority-black:#1F2937;--bg-primary:var(--c-page);--bg-secondary:var(--c-card);--bg-tertiary:var(--c-column);--bg-input:var(--c-input);--border-color:var(--c-border);--border-input:var(--c-border-strong);--text-primary:var(--c-text);--text-secondary:var(--c-muted);--text-label:var(--c-muted);--accent:var(--c-accent);--accent-hover:var(--c-accent-hover);--primary:var(--c-accent);--bg:var(--c-card);--text:var(--c-text);--border:var(--c-border);--chev:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%235F6875' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5l3 3 3-3'/%3E%3C/svg%3E");color-scheme:light}[data-theme=dark]{--c-page:#0E1013;--c-column:#14171C;--c-card:#1B1F26;--c-input:#1B1F26;--c-pop:#232831;--c-hover:rgba(255, 255, 255, 0.05);--c-fill:rgba(148, 163, 184, 0.16);--c-border:#252A33;--c-border-strong:#343B47;--c-text:#E8EAEE;--c-muted:#A0A8B5;--c-faint:#707988;--c-accent:#7B9BFF;--c-accent-hover:#9BB3FF;--c-accent-soft:rgba(123, 155, 255, 0.16);--c-on-accent:#0E1013;--c-danger:#EF6B6B;--c-warn:#E0A94A;--c-ok:#4CC983;--c-overlay:rgba(0, 0, 0, 0.55);--shadow-pop:0 10px 30px rgba(0, 0, 0, 0.5),0 0 0 1px rgba(255, 255, 255, 0.07);--c-code-text:#E8EAEE;--logo-tile:#1B1F26;--logo-ring:#343B47;--logo-hash:#7B9BFF;--logo-tall:#E8EAEE;--logo-short:#7B9BFF;--priority-default:#707988;--priority-red:#EF6B6B;--priority-orange:#FF8A3D;--priority-yellow:#F2CC3A;--priority-green:#4CC983;--priority-blue:#6EA8FF;--priority-purple:#B197F0;--priority-white:#5B6372;--priority-black:#8B93A1;--chev:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%23A0A8B5' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5l3 3 3-3'/%3E%3C/svg%3E");color-scheme:dark}body,html{height:100%}body{display:flex;flex-direction:column;overflow:hidden;font-family:var(--font);font-size:var(--fs-md);line-height:1.5;background:var(--c-page);color:var(--c-text);-webkit-font-smoothing:antialiased}:focus-visible{outline:2px solid var(--c-accent);outline-offset:2px}.btn{height:var(--control-h);padding:0 var(--s-3);border:1px solid transparent;border-radius:var(--r-sm);font:inherit;font-size:var(--fs-sm);font-weight:500;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);white-space:nowrap;transition:background .12s,border-color .12s}.btn-primary{background:var(--c-accent);color:var(--c-on-accent)}.btn-primary:hover{background:var(--c-accent-hover)}.btn-secondary{background:var(--c-card);color:var(--c-text);border-color:var(--c-border-strong)}.btn-secondary:hover{background:var(--c-hover)}.btn-ghost{background:0 0;color:var(--c-muted)}.btn-ghost:hover{background:var(--c-hover);color:var(--c-text)}.btn-danger{background:0 0;color:var(--c-danger);border-color:var(--c-border-strong)}.btn-danger:hover{background:var(--c-danger);border-color:var(--c-danger);color:#fff}.btn-icon{width:var(--control-h);padding:0}.btn-sm{height:26px;padding:0 var(--s-2);font-size:var(--fs-xs)}.btn:disabled{opacity:.45;cursor:default}.ic{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}input[type=date],input[type=text],select,textarea{width:100%;height:var(--control-h);padding:0 var(--s-3);border:1px solid var(--c-border-strong);border-radius:var(--r-sm);background:var(--c-input);color:var(--c-text);font:inherit;font-size:var(--fs-md)}select{appearance:none;-webkit-appearance:none;padding-right:32px;background-image:var(--chev);background-repeat:no-repeat;background-position:right 12px center;background-size:12px;cursor:pointer}textarea{height:auto;padding:var(--s-2) var(--s-3);resize:vertical;line-height:1.5}input::placeholder,textarea::placeholder{color:var(--c-faint)}input:focus,select:focus,textarea:focus{outline:0;border-color:var(--c-accent);box-shadow:0 0 0 3px var(--c-accent-soft)}label{font-size:var(--fs-sm);font-weight:500;color:var(--c-muted)}small{font-size:var(--fs-xs);color:var(--c-faint)}.header{flex:none;position:relative;z-index:100;height:52px;padding:0 var(--s-4);display:flex;align-items:center;gap:var(--s-3);background:var(--c-column);border-bottom:1px solid var(--c-border)}.header h1{font-size:var(--fs-lg);font-weight:600;letter-spacing:-.01em;white-space:nowrap;display:inline-flex;align-items:center;gap:var(--s-2)}.app-logo{width:24px;height:24px;flex:none;border-radius:6px}#projectDd{width:auto;min-width:160px;max-width:260px}#projectDd .dd-menu{right:auto;min-width:100%;width:max-content;max-width:360px}.header-spacer{flex:1}#filterBar{display:none;position:relative;flex:100 1 0;min-width:0;max-width:820px;margin:0 auto;container:filterbar/inline-size}.filter-row{display:flex;align-items:center;gap:var(--s-2)}.search-field{position:relative;flex:1 1 160px;min-width:120px}.search-field .ic{position:absolute;left:var(--s-3);top:50%;transform:translateY(-50%);color:var(--c-faint);pointer-events:none}.search-field input{padding-left:36px;padding-right:32px}#clearGlobalSearch{position:absolute;right:2px;top:50%;transform:translateY(-50%);width:28px;height:28px;border:0;background:0 0;color:var(--c-faint);cursor:pointer;font-size:var(--fs-lg);display:none}.filter-count{min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:var(--c-accent);color:var(--c-on-accent);font-size:var(--fs-xs);line-height:18px;text-align:center}.filter-group{display:flex;align-items:center;gap:var(--s-1);flex:none}.dd{position:relative}.dd-menu{display:none;position:absolute;z-index:300}.dd.open>.dd-menu{display:block}.dd-field .dd-trigger{width:100%;height:var(--control-h);padding:0 32px 0 var(--s-3);display:flex;align-items:center;gap:var(--s-2);text-align:left;border:1px solid var(--c-border-strong);border-radius:var(--r-sm);background:var(--c-input) var(--chev) no-repeat right 12px center/12px;color:var(--c-text);font:inherit;font-size:var(--fs-md);cursor:pointer}.dd-field .dd-trigger:focus-visible,.dd-field.open .dd-trigger{outline:0;border-color:var(--c-accent);box-shadow:0 0 0 3px var(--c-accent-soft)}.dd-field .dd-opt>span:not(.prio),.dd-field .dd-trigger>span:not(.prio){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dd-field .dd-menu{top:calc(100% + var(--s-1));left:0;right:0;max-height:260px;overflow-y:auto;padding:var(--s-1);background:var(--c-pop);border-radius:var(--r-md);box-shadow:var(--shadow-pop)}.dd-opt{display:flex;align-items:center;gap:var(--s-2);height:32px;padding:0 var(--s-2);border-radius:var(--r-sm);font-size:var(--fs-md);color:var(--c-text);cursor:pointer;outline:0}.dd-opt:focus,.dd-opt:hover{background:var(--c-hover)}.dd-opt[aria-selected=true]{font-weight:600}.dd .prio{cursor:inherit}.dd-compact{min-width:120px}.dd-compact .dd-trigger{height:28px;font-size:var(--fs-sm)}.dd-compact .dd-menu{left:auto;min-width:100%;right:0}.dd-compact .dd-opt{height:28px;font-size:var(--fs-sm);white-space:nowrap}.prio-none{width:16px;height:16px;border-radius:50%;border:1.5px dashed var(--c-faint);box-sizing:border-box;flex:none}.fbtn{height:var(--control-h);padding:0 var(--s-2) 0 var(--s-3);display:inline-flex;align-items:center;gap:var(--s-1);border:1px solid var(--c-border-strong);border-radius:var(--r-sm);background:var(--c-card);color:var(--c-muted);font:inherit;font-size:var(--fs-sm);font-weight:500;cursor:pointer;white-space:nowrap}.fbtn:hover{background:var(--c-hover);color:var(--c-text)}.fbtn .chev{width:12px;height:12px;color:var(--c-faint)}.fcount{min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:var(--c-accent);color:var(--c-on-accent);font-size:var(--fs-xs);line-height:16px;text-align:center}.fcount:empty{display:none}.fdrop.active .fbtn{border-color:var(--c-accent);background:var(--c-accent-soft);color:var(--c-accent)}.fmenu{top:calc(100% + var(--s-1));left:0;z-index:200;min-width:200px;max-width:280px;max-height:280px;overflow-y:auto;padding:var(--s-1);background:var(--c-pop);border-radius:var(--r-md);box-shadow:var(--shadow-pop)}.fopt{display:flex;align-items:center;gap:var(--s-2);height:30px;padding:0 var(--s-2);border-radius:var(--r-sm);font-size:var(--fs-sm);color:var(--c-text);font-weight:400;cursor:pointer}.fopt:hover{background:var(--c-hover)}.fopt input{width:14px;height:14px;accent-color:var(--c-accent);flex:none}.fopt span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fempty{padding:var(--s-2);font-size:var(--fs-xs);color:var(--c-faint)}#filterToggleBtn{display:none}@container filterbar (max-width:579px){#filterToggleBtn{display:inline-flex}.filter-group{display:none;position:absolute;top:calc(100% + var(--s-2));right:0;z-index:200;width:300px;max-width:100cqw;max-height:calc(100vh - 80px);overflow-y:auto;flex-direction:column;align-items:stretch;gap:var(--s-1);padding:var(--s-2);background:var(--c-pop);border-radius:var(--r-md);box-shadow:var(--shadow-pop)}.filter-group.open{display:flex}.fdrop+.fdrop{border-top:1px solid var(--c-border);padding-top:var(--s-1)}.fbtn{width:100%;border:0;background:0 0;padding:0 var(--s-2);height:28px;font-weight:600;color:var(--c-text)}.fbtn .chev{margin-left:auto;display:none}.fcount{margin-left:auto}.fdrop.active .fbtn{background:0 0;color:var(--c-text)}.fmenu{display:block;position:static;box-shadow:none;padding:0 0 var(--s-1);min-width:0;max-width:none;max-height:190px}}.popover{display:none;position:absolute;z-index:200;background:var(--c-pop);border-radius:var(--r-md);box-shadow:var(--shadow-pop);padding:var(--s-3)}.popover.open{display:block}.field{display:flex;flex-direction:column;gap:var(--s-1)}.menu-wrap{position:relative}#moreMenu,#themeMenu{top:calc(100% + var(--s-2));min-width:200px;padding:var(--s-1)}#moreMenu{left:0}#themeMenu{right:0;min-width:160px}.menu-item .check{width:14px;color:var(--c-accent);font-size:var(--fs-sm)}#moreMenu .menu-item .check{display:none}.menu-item{width:100%;height:32px;padding:0 var(--s-3);display:flex;align-items:center;gap:var(--s-2);border:0;border-radius:var(--r-sm);background:0 0;color:var(--c-text);font:inherit;font-size:var(--fs-sm);cursor:pointer;text-align:left}a.menu-item{text-decoration:none}.menu-item .ic{color:var(--c-muted)}.menu-item.danger .ic{color:inherit}.menu-item:hover{background:var(--c-hover)}.menu-item.danger{color:var(--c-danger)}.menu-sep{height:1px;margin:var(--s-2) 0;background:var(--c-border-strong)}.menu-row{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);padding:var(--s-1) var(--s-3);font-size:var(--fs-sm);color:var(--c-muted)}.menu-label{display:inline-flex;align-items:center;gap:var(--s-2);color:var(--c-text);font-weight:400}.menu-label .ic{color:var(--c-muted)}#activeFilters{flex:none;display:flex;flex-wrap:wrap;gap:var(--s-2);padding:0 var(--s-4)}#activeFilters:not(:empty){padding-top:var(--s-3)}.chip{display:inline-flex;align-items:center;gap:var(--s-1);height:24px;padding:0 var(--s-2);border-radius:12px;background:var(--c-accent-soft);color:var(--c-accent);font-size:var(--fs-xs);font-weight:500}.chip-clear{height:24px;padding:0 var(--s-2);border:0;border-radius:12px;background:0 0;color:var(--c-muted);font:inherit;font-size:var(--fs-xs);font-weight:500;cursor:pointer}.chip-clear:hover{background:var(--c-hover);color:var(--c-text)}#activeFilters{align-items:center}.chip button{border:0;background:0 0;color:inherit;cursor:pointer;font-size:var(--fs-md);line-height:1;padding:0 2px}.container{flex:0 1 auto;min-height:0;overflow:auto;width:100%;max-width:1400px;margin:0 auto;padding:0 var(--s-5)}.welcome{text-align:center;padding:var(--s-6) var(--s-4)}.welcome h2{font-size:var(--fs-xl);margin-bottom:var(--s-2)}.welcome p{color:var(--c-muted);margin-bottom:var(--s-5)}.welcome .welcome-alt{margin:var(--s-2) 0 0}.welcome-card{margin:var(--s-5) auto 0;padding:var(--s-5);background:var(--c-card);border:1px solid var(--c-border);border-radius:var(--r-md);max-width:560px;text-align:left}.welcome-card h3{font-size:var(--fs-md);margin-bottom:var(--s-3)}.welcome-card ol{margin-left:var(--s-5);color:var(--c-muted);display:flex;flex-direction:column;gap:var(--s-1)}.welcome-card .note{margin-top:var(--s-4);font-size:var(--fs-sm);color:var(--c-faint)}.welcome-card .note.warn{color:var(--c-muted);padding-left:var(--s-2);border-left:3px solid var(--c-warn)}#kanbanView{flex:1;min-height:0;margin:0;padding:0;overflow:auto}#newTaskBtn::before{content:"";flex:none;width:14px;height:14px;background:currentColor;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M8 3v10M3 8h10'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M8 3v10M3 8h10'/%3E%3C/svg%3E") center/contain no-repeat}.kanban-board{display:flex;gap:var(--s-3);padding:0 var(--s-4) var(--s-4);width:100%;min-height:100%;align-items:stretch}.kanban-board::after{content:"";flex:0 0 var(--s-1)}.kanban-column{flex:1 0 var(--col-min);max-width:var(--col-max);margin-top:var(--s-3);background:var(--c-column);border-radius:var(--r-md);display:flex;flex-direction:column}.column-header{position:sticky;top:0;z-index:2;flex:none;padding:var(--s-3) var(--s-3) var(--s-2);background:var(--c-column);border-radius:var(--r-md) var(--r-md) 0 0;border-bottom:1px solid transparent;transition:border-color .15s;display:flex;justify-content:space-between;align-items:center}#kanbanView.is-scrolled .column-header{border-bottom-color:var(--c-border)}.column-title{font-size:var(--fs-sm);font-weight:600;text-transform:none;color:var(--c-text);display:flex;align-items:center;gap:var(--s-2)}.column-tools{display:flex;align-items:center;gap:var(--s-1)}.column-count{font-size:var(--fs-xs);color:var(--c-faint);font-variant-numeric:tabular-nums}.column-add-btn{width:24px;height:24px;border:0;border-radius:var(--r-sm);background:0 0;color:var(--c-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;opacity:0;transition:opacity .12s,background .12s}.column-add-btn:focus-visible,.kanban-column:hover .column-add-btn{opacity:1}.column-add-btn:hover{background:var(--c-hover);color:var(--c-text)}.task-list{min-height:48px;flex:1;display:flex;flex-direction:column;gap:var(--s-2);padding:0 var(--s-2) var(--s-2)}.empty-state{padding:var(--s-4) var(--s-3);border:1px dashed var(--c-border-strong);border-radius:var(--r-sm);text-align:center;font-size:var(--fs-xs);color:var(--c-faint)}.task-card{position:relative;background:var(--c-card);border:1px solid var(--c-border);border-radius:var(--r-sm);padding:var(--s-3);cursor:pointer;display:flex;flex-direction:column;gap:var(--s-2);transition:border-color .12s,box-shadow .12s}.task-card:hover{border-color:var(--c-border-strong);box-shadow:0 1px 4px rgba(15,18,24,.08)}.task-card.dragging{opacity:.5}.task-card--soon{border-left:3px solid var(--c-warn)}.task-card--overdue{border-left:3px solid var(--c-danger)}.kanban-column.drop-target{outline:2px dashed var(--c-accent);outline-offset:-4px}.task-header{display:flex;align-items:center;gap:var(--s-2);min-height:20px}.task-id{font-size:var(--fs-xs);color:var(--c-faint);font-family:var(--mono)}.prio-label{display:inline-flex;align-items:center;gap:6px}.prio-label .prio{cursor:inherit}.prio{display:inline-flex;width:16px;height:16px;color:var(--priority-default);flex:none;cursor:pointer}.prio svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.prio.Red{color:var(--priority-red)}.prio.Orange{color:var(--priority-orange)}.prio.Yellow{color:var(--priority-yellow)}.prio.Green{color:var(--priority-green)}.prio.Blue{color:var(--priority-blue)}.prio.Purple{color:var(--priority-purple)}.prio.White{color:var(--priority-white)}.prio.Black{color:var(--priority-black)}.task-edit-btn{margin-left:auto;width:24px;height:24px;border:0;border-radius:var(--r-sm);background:0 0;color:var(--c-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;opacity:0;transition:opacity .12s,background .12s}.task-card:hover .task-edit-btn,.task-edit-btn:focus-visible{opacity:1}.task-edit-btn:hover{background:var(--c-hover);color:var(--c-text)}@media (hover:none){.column-add-btn,.task-edit-btn{opacity:.6}}.task-title{font-size:var(--fs-md);font-weight:600;line-height:1.35;color:var(--c-text)}.task-description{font-size:var(--fs-sm);color:var(--c-muted);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.task-description *{display:inline;margin:0}.task-meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-1) var(--s-2);font-size:var(--fs-xs)}.badge{padding:1px var(--s-2);border-radius:var(--r-sm);font-size:var(--fs-xs);font-weight:500;background:var(--c-fill);color:var(--c-muted)}.badge[data-filter],.tag[data-filter]{cursor:pointer}.badge[data-filter]:hover{background:var(--c-hover);color:var(--c-text)}.badge-due--soon{background:0 0;color:var(--c-warn)}.badge-due--overdue{background:0 0;color:var(--c-danger)}.tag{color:var(--c-faint);font-size:var(--fs-xs)}.tag[data-filter]:hover{color:var(--c-accent)}.task-subtasks{font-size:var(--fs-xs);color:var(--c-faint)}.subtask-progress{display:flex;align-items:center;gap:var(--s-2);font-variant-numeric:tabular-nums}.progress-bar{flex:1;height:3px;background:var(--c-fill);border-radius:2px;overflow:hidden}.progress-fill{height:100%;background:var(--c-ok);transition:width .3s}pre{margin:var(--s-3) 0;border-radius:var(--r-sm);overflow-x:auto;background:var(--c-code-bg);padding:var(--s-3)}pre code{font-family:var(--mono);font-size:var(--fs-sm);line-height:1.5;color:var(--c-code-text);display:block;background:0 0;padding:0}code{background:var(--c-code-bg);color:var(--c-code-text);padding:1px 5px;border-radius:4px;font-family:var(--mono);font-size:.9em}.modal{display:none;position:fixed;inset:0;background:var(--c-overlay);z-index:1000;align-items:center;justify-content:center;padding:var(--s-4)}.modal.active{display:flex}.modal-content{background:var(--c-column);border-radius:var(--r-md);width:100%;max-width:680px;max-height:calc(100vh - var(--s-6));overflow-y:auto;box-shadow:var(--shadow-pop)}.modal-content.wide{max-width:880px}.modal-content.narrow{max-width:480px}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--c-border)}.modal-header h2{font-size:var(--fs-md);font-weight:600}.close-btn{width:28px;height:28px;border:0;border-radius:var(--r-sm);background:0 0;color:var(--c-muted);font-size:var(--fs-xl);line-height:1;cursor:pointer}.close-btn:hover{background:var(--c-hover);color:var(--c-text)}.modal-body{padding:var(--s-4)}.form-stack{display:flex;flex-direction:column;gap:var(--s-3);padding:var(--s-4)}.form-grid{display:grid;gap:var(--s-3)}.form-grid.cols-2{grid-template-columns:1fr 1fr}.form-grid.cols-4{grid-template-columns:repeat(4,1fr)}#taskNotes{font-family:var(--mono);font-size:var(--fs-sm)}.actions{display:flex;gap:var(--s-2);justify-content:flex-end;padding:var(--s-3) var(--s-4);border-top:1px solid var(--c-border)}.actions:empty{display:none}.form-stack .actions{padding:var(--s-3) 0 0}.actions .push{margin-right:auto;display:flex;gap:var(--s-2)}.detail{padding:var(--s-4);display:flex;flex-direction:column;gap:var(--s-4)}.detail-id{font-family:var(--mono);font-size:var(--fs-xs);color:var(--c-faint)}.detail h3{font-size:var(--fs-xl);line-height:1.25;font-weight:600;margin-top:var(--s-1)}.detail-meta{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--s-3) var(--s-4)}.detail-label,.detail-meta dt{font-size:var(--fs-xs);color:var(--c-faint);text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px}.detail-meta dd{font-size:var(--fs-md);font-weight:500}.detail-tags{display:flex;gap:var(--s-2);flex-wrap:wrap}.detail-tags span{color:var(--c-accent);background:var(--c-accent-soft);padding:1px var(--s-2);border-radius:10px;font-size:var(--fs-sm)}.detail-text{color:var(--c-text);line-height:1.6}.detail-notes{background:var(--c-card);border-radius:var(--r-sm);padding:var(--s-3);line-height:1.6}.subtask-list{list-style:none;display:flex;flex-direction:column}.subtask-item{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-1) var(--s-2);border-radius:var(--r-sm)}.subtask-item:hover{background:var(--c-hover)}.subtask-item input[type=checkbox]{width:16px;height:16px;accent-color:var(--c-accent);cursor:pointer;flex:none}.subtask-item span{flex:1;cursor:pointer}.subtask-item.done span{color:var(--c-faint);text-decoration:line-through;text-decoration-color:var(--c-border-strong)}.subtask-item .del{border:0;background:0 0;color:var(--c-faint);cursor:pointer;padding:var(--s-1);border-radius:var(--r-sm);opacity:0;display:inline-flex}.subtask-item .del:focus-visible,.subtask-item:hover .del{opacity:1}.subtask-item .del:hover{color:var(--c-danger)}.subtask-add{display:flex;gap:var(--s-2);margin-top:var(--s-2)}.list-rows{display:flex;flex-direction:column;gap:var(--s-2)}.row-card{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-2);border:1px solid var(--c-border);border-radius:var(--r-sm);background:var(--c-card)}.row-card .grow{flex:1;min-width:0}.row-card input[type=text]{height:28px}.row-card input.col-id{width:120px;font-family:var(--mono);font-size:var(--fs-sm)}.archive-item{background:var(--c-card);border:1px solid var(--c-border);border-radius:var(--r-sm);padding:var(--s-3);display:flex;flex-direction:column;gap:var(--s-2)}.archive-item .top{display:flex;justify-content:space-between;align-items:center;gap:var(--s-3)}.archive-item .top strong{font-size:var(--fs-md)}.archive-item .desc{color:var(--c-muted);font-size:var(--fs-sm)}.notification{position:fixed;bottom:var(--s-5);right:var(--s-5);background:var(--c-pop);color:var(--c-text);padding:var(--s-3) var(--s-4);border-radius:var(--r-sm);box-shadow:var(--shadow-pop);display:none;align-items:center;gap:var(--s-3);z-index:1001;border-left:3px solid var(--c-border-strong)}.notification.show{display:flex;animation:slideIn .2s ease}.notification.success{border-left-color:var(--c-ok)}.notification.error{border-left-color:var(--c-danger)}@keyframes slideIn{from{transform:translateY(8px);opacity:0}to{transform:none;opacity:1}}.loading{display:inline-block;width:18px;height:18px;border:2px solid var(--c-border);border-top-color:var(--c-accent);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.debug-info{background:var(--c-column);border:1px solid var(--c-border);padding:var(--s-3);margin:var(--s-3);border-radius:var(--r-sm);font-family:var(--mono);font-size:var(--fs-sm);white-space:pre-wrap;word-wrap:break-word}.hk-bar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2) var(--s-4);margin:0 var(--s-4) var(--s-3);padding:var(--s-2) var(--s-3);background:var(--c-accent-soft);border:1px solid var(--c-border);border-radius:var(--r-md);font-size:var(--fs-sm);color:var(--c-text)}.hk-bar .hk-msgs{flex:1 1 280px;display:flex;flex-wrap:wrap;gap:var(--s-1) var(--s-4);color:var(--c-muted)}.hk-bar .hk-actions{display:flex;gap:var(--s-2)}.task-card.hk-stale{outline:1px dashed var(--c-warn);outline-offset:-1px}.kanban-column.is-rail{flex:0 0 var(--rail-w);min-width:0;overflow:clip;transition:flex-basis .18s ease}.kanban-column.is-rail.is-open{flex-basis:var(--rail-open-w)}.is-rail{cursor:pointer}.is-rail .column-header{justify-content:center}.is-rail .column-tools,.is-rail .rail-title-words{display:none}.is-rail.is-open .column-header{justify-content:space-between}.is-rail.is-open .column-tools{display:flex}.is-rail.is-open .rail-title-words{display:inline}.is-rail .task-list{padding:0 var(--s-1) var(--s-2)}.rail-stub{position:relative;min-width:calc(var(--rail-open-w) - 2 * var(--s-2))}.rail-button{width:calc(var(--rail-w) - 2 * var(--s-1));display:flex;flex-direction:column;align-items:center;gap:var(--s-2);padding:var(--s-2) 0;background:0 0;border:0;border-radius:var(--r-sm);color:var(--c-muted);cursor:pointer;font:inherit}.is-rail:active .rail-button{color:var(--c-text)}@media (hover:hover){.kanban-column.is-rail:hover .rail-button{color:var(--c-text)}}.kanban-column.is-rail:active{background-image:linear-gradient(var(--c-hover),var(--c-hover))}@media (hover:hover){.kanban-column.is-rail:hover{background-image:linear-gradient(var(--c-hover),var(--c-hover))}}.rail-count{font-size:var(--fs-sm);font-weight:600;color:var(--c-text)}.rail-name{writing-mode:vertical-rl;transform:rotate(180deg);font-size:var(--fs-sm);white-space:nowrap}.rail-stub-body{display:none;flex-direction:column;align-items:center;gap:var(--s-2);padding:var(--s-5) var(--s-3);text-align:center;color:var(--c-muted)}.is-rail.is-open .rail-stub-body{display:flex}.is-rail.is-open .rail-button{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%)}.rail-button:focus-visible~.rail-stub-body{outline:2px solid var(--c-accent);outline-offset:2px}.rail-stub-count{font-size:var(--fs-xl);font-weight:600;color:var(--c-text)}.rail-stub-note{font-size:var(--fs-sm)}.rail-stub-hint{font-size:var(--fs-xs);color:var(--c-faint)}@media (min-width:769px){.kanban-column.is-rail:focus-within{flex-basis:var(--rail-open-w)}.is-rail:focus-within .column-header{justify-content:space-between}.is-rail:focus-within .column-tools{display:flex}.is-rail:focus-within .rail-title-words{display:inline}.is-rail:focus-within .rail-stub-body{display:flex}.is-rail:focus-within .rail-button{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%)}}@media (min-width:769px) and (hover:hover){.kanban-column.is-rail:hover{flex-basis:var(--rail-open-w)}.is-rail:hover .column-header{justify-content:space-between}.is-rail:hover .column-tools{display:flex}.is-rail:hover .rail-title-words{display:inline}.is-rail:hover .rail-stub-body{display:flex}.is-rail:hover .rail-button{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%)}}.archive-item.hk-stale{border-style:dashed;border-color:var(--c-warn)}.share-row{display:flex;gap:var(--s-2);margin-bottom:var(--s-3)}.share-preview{width:100%;resize:vertical;font-family:var(--mono);font-size:var(--fs-sm);line-height:1.5;padding:var(--s-3);border:1px solid var(--c-border-strong);border-radius:var(--r-md);background:var(--c-input);color:var(--c-text)}.share-opt{display:flex;align-items:center;gap:var(--s-2);margin-top:var(--s-3);font-size:var(--fs-sm);color:var(--c-text);cursor:pointer}.share-hint{margin-top:var(--s-3);font-size:var(--fs-sm);color:var(--c-muted)}.share-view{display:inline-flex}.share-view .btn{border-radius:0;padding-inline:var(--s-3)}.share-view .btn+.btn{margin-left:-1px}.share-view .btn:first-child{border-top-left-radius:var(--r-md);border-bottom-left-radius:var(--r-md)}.share-view .btn:last-child{border-top-right-radius:var(--r-md);border-bottom-right-radius:var(--r-md)}.share-view .btn.btn-primary{position:relative;z-index:1}.share-rendered{max-height:22rem;overflow:auto;padding:var(--s-3);border:1px solid var(--c-border-strong);border-radius:var(--r-md);background:var(--c-input);color:var(--c-text);font-size:var(--fs-sm);line-height:1.5}.share-rendered h1,.share-rendered h2,.share-rendered h3{margin:0 0 var(--s-2);padding-bottom:var(--s-1);border-bottom:1px solid var(--c-border-strong);font-size:1.15em}.share-rendered p{margin:0 0 var(--s-3)}.share-rendered ol,.share-rendered ul{margin:0 0 var(--s-3);padding-left:1.5em}.share-rendered li.task-list-item,.share-rendered li:has(> input[type=checkbox]){list-style:none;margin-left:-1.2em}.share-rendered input[type=checkbox]{margin-right:var(--s-2)}.share-rendered code{font-family:var(--mono);font-size:.9em;padding:.1em .35em;border-radius:4px;background:var(--c-border)}.share-rendered pre{padding:var(--s-3);border-radius:var(--r-md);background:var(--c-border);overflow:auto}.share-rendered pre code{padding:0;background:0 0}.share-rendered hr{border:0;border-top:1px solid var(--c-border-strong);margin:var(--s-3) 0}.share-rendered a{color:var(--primary);text-decoration:underline}.share-rendered blockquote{margin:0 0 var(--s-3);padding-left:var(--s-3);border-left:3px solid var(--c-border-strong);color:var(--c-muted)}.status-bar{flex:none;display:flex;align-items:center;gap:var(--s-3);min-height:28px;padding:2px var(--s-4);background:var(--c-column);border-top:1px solid var(--c-border);font-size:var(--fs-xs);color:var(--c-muted)}.status-left{display:flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:0 var(--s-3);min-width:0}.status-left>*+*{position:relative}.status-left>*+::before{content:"";position:absolute;left:calc(var(--s-3)/ -2);top:50%;width:1px;height:14px;transform:translateY(-50%);background:var(--c-border-strong)}.status-seg{display:inline-flex;align-items:center;gap:var(--s-2);min-width:0}.status-badge,.status-seg strong{display:inline-flex;align-items:center;gap:var(--s-1);font-weight:600;color:var(--c-text)}.status-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--c-muted)}a.status-label:hover{color:var(--c-text)}.status-sync{flex:none;font-variant-numeric:tabular-nums}.status-refresh{display:inline-flex;align-items:center;gap:var(--s-2);align-self:stretch;margin:-2px calc(var(--s-3) * -1) -2px 0;padding:0 var(--s-3);border:0;background:0 0;color:inherit;font:inherit;cursor:pointer}.status-refresh:focus-visible,.status-refresh:hover:not(:disabled){background:var(--c-fill);color:var(--c-text)}.status-refresh:disabled{cursor:progress}.status-refresh-icon{display:inline-flex}.status-refresh .ic{width:14px;height:14px}.status-refresh.is-busy .ic{animation:status-spin .8s linear infinite}@keyframes status-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.status-refresh.is-busy .ic{animation:none}}.status-bar .btn-sm{height:22px}.status-chip{gap:var(--s-2)}.status-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--c-ok)}.status-dot--stopped{background:var(--c-danger)}.status-dot--expired{background:var(--c-warn)}body.has-status .notification{bottom:calc(var(--s-5) + 28px)}.note-serve{margin-top:var(--s-3);color:var(--c-muted);font-size:var(--fs-sm)}.note-serve a{color:var(--c-accent)}.steps{margin:var(--s-3) 0 0 var(--s-5);display:flex;flex-direction:column;gap:var(--s-3)}.cmd-row{display:flex;align-items:center;gap:var(--s-2);margin-top:var(--s-2);padding:var(--s-2) var(--s-3);background:var(--c-fill);border:1px solid var(--c-border);border-radius:var(--r-sm)}.cmd-row code{flex:1;min-width:0;font-family:Consolas,Monaco,monospace;font-size:var(--fs-sm);color:var(--c-text);user-select:all;overflow-x:auto;white-space:nowrap}body.readonly #housekeepingBar,body.readonly #manageColsBtn,body.readonly #newTaskBtn{display:none!important}.welcome-card.web-notice{margin:0 auto var(--s-5)}.web-notice p{margin-bottom:var(--s-3)}.web-notice .web-notice-url{font-size:var(--fs-sm);word-break:break-all}.web-notice .web-notice-url a{color:var(--c-muted)}.web-notice .web-notice-actions{display:flex;justify-content:flex-end;gap:var(--s-2)}.web-notice--error{border-color:var(--c-danger)}.web-notice--error h3{color:var(--c-danger)}.badge-blocked{display:inline-flex;align-items:center;gap:var(--s-1);font-variant-numeric:tabular-nums}.badge-blocked .ic{width:11px;height:11px;color:var(--c-warn)}.task-link{color:var(--c-accent);text-decoration:none}.task-link:hover{text-decoration:underline}.link-chips{display:flex;flex-wrap:wrap;gap:var(--s-1) var(--s-2)}.link-chip{display:inline-flex;align-items:center;gap:var(--s-1);padding:1px var(--s-2) 1px var(--s-1);border:1px solid var(--c-border-strong);border-radius:var(--r-sm);background:0 0;color:var(--c-text);font-size:var(--fs-sm);font-family:var(--mono);text-decoration:none}a.link-chip:hover{background:var(--c-hover)}.link-chip--muted{opacity:.55}.link-ic{display:inline-flex;align-items:center;justify-content:center;width:1.25em;font-family:var(--font)}.link-dot::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.5}.link-banner{margin-top:var(--s-2);padding:var(--s-2) var(--s-3);border-radius:var(--r-sm);border:1px solid color-mix(in srgb,var(--c-warn) 40%,transparent);background:color-mix(in srgb,var(--c-warn) 10%,transparent);color:var(--c-text);font-size:var(--fs-sm)}.link-action{border:0;background:0 0;padding:0;margin-left:var(--s-1);color:var(--c-accent);font:inherit;cursor:pointer}.link-action:hover{text-decoration:underline}.link-error{display:block;color:var(--c-danger);margin-top:var(--s-1)}.detail-back{display:flex;align-items:center;gap:var(--s-2);width:100%;padding:var(--s-2) var(--s-3);border:1px solid color-mix(in srgb,var(--c-accent) 30%,transparent);border-radius:var(--r-sm);background:color-mix(in srgb,var(--c-accent) 10%,transparent);color:var(--c-accent);font:inherit;text-align:left;cursor:pointer}.detail-back:hover{background:color-mix(in srgb,var(--c-accent) 16%,transparent)}.detail-back-arrow{font-size:var(--fs-lg);line-height:1}.detail-back-text{display:flex;flex-direction:column;min-width:0;flex:1}.detail-back-title{color:var(--c-text);font-size:var(--fs-sm);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.detail-back-meta{color:var(--c-muted);font-size:var(--fs-xs);font-family:var(--mono)}.link-warn{margin-top:var(--s-1);font-size:var(--fs-sm);color:var(--c-warn)}#taskBlockedByField .chips{display:flex;flex-wrap:wrap;gap:var(--s-1) var(--s-2);margin-bottom:var(--s-2)}#taskBlockedByField .chips:empty{display:none}.chip{display:inline-flex;align-items:center;gap:var(--s-1);padding:1px var(--s-1) 1px var(--s-2);border:1px solid var(--c-border-strong);border-radius:var(--r-sm);background:0 0;color:var(--c-text);font-size:var(--fs-sm);max-width:100%}.chip button{border:0;background:0 0;color:var(--c-faint);cursor:pointer;font-size:var(--fs-md);line-height:1;padding:0 var(--s-1)}.chip button:hover{color:var(--c-text)}@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}@media (max-width:900px){.header{flex-wrap:wrap;height:auto;padding:var(--s-2) var(--s-3)}#filterBar{order:3;flex:1 1 100%;max-width:none}.header-spacer{display:none}.header h1{margin-right:auto}}@media (max-width:768px){#kanbanView{scroll-snap-type:x proximity}.kanban-board{padding:0 var(--s-3) var(--s-3);gap:var(--s-2)}.kanban-column{flex:0 0 85vw;scroll-snap-align:start}.form-grid.cols-2,.form-grid.cols-4{grid-template-columns:1fr}.detail-meta{grid-template-columns:1fr 1fr}}@media (max-width:480px){.kanban-column{flex:0 0 92vw}}</style></head><body><header class="header"><div class="menu-wrap"><button class="btn btn-ghost btn-icon" id="moreMenuBtn" onclick="toggleMoreMenu(event)" aria-haspopup="true" aria-expanded="false" title="Menu" aria-label="Menu"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M2.5 4h11M2.5 8h11M2.5 12h11"/></svg></button><div class="popover" id="moreMenu" onclick="event.stopPropagation()"><button id="selectFolderBtn" class="menu-item" onclick="closeMenus()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 4.5h4l1.5 1.5H14v6.5H2z"/></svg><span class="mi-label">Folder</span></button> <button id="openUrlBtn" class="menu-item" onclick="closeMenus(); openBoardFromUrl()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6"/><path d="M2 8h12M8 2c2 2 2 10 0 12M8 2c-2 2-2 10 0 12"/></svg><span class="mi-label">Open from a web address…</span></button> <button id="serviceBackBtn" class="menu-item" style="display:none" onclick="closeMenus(); backToServiceBoard()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M9.5 3.5 5 8l4.5 4.5M5.5 8H14"/></svg><span class="mi-label">Back to served board</span></button> <button id="pickedEditBtn" class="menu-item" style="display:none" onclick="closeMenus(); openServeHelp()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M10.5 2.5l3 3L5 14H2v-3z"/></svg><span class="mi-label">Edit with kanbanmd</span></button><div class="menu-sep"></div><button id="archiveBtn" class="menu-item" style="display:none" onclick="closeMenus()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 4h12v3H2zM3 7v6h10V7M6.5 9.5h3"/></svg><span class="mi-label">Archives</span></button> <button id="manageColsBtn" class="menu-item" style="display:none" onclick="closeMenus()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M2.5 3h3v10h-3zM6.5 3h3v10h-3zM10.5 3h3v10h-3z"/></svg><span class="mi-label">Columns</span></button> <button id="orderToggleBtn" class="menu-item" style="display:none" onclick="closeMenus(); toggleManualOrder()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M5 3v10M3 11l2 2 2-2M11 13V3M9 5l2-2 2 2"/></svg><span class="mi-label">Manual order</span></button> <button id="orderResetBtn" class="menu-item" style="display:none" onclick="closeMenus(); resetOrderById()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4h7M3 8h5M3 12h3M12 5v7M10 10l2 2 2-2"/></svg><span class="mi-label">Reset order by task id</span></button> <button id="housekeepingBtn" class="menu-item" style="display:none" onclick="closeMenus(); editHousekeepingSettings()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M5 4.5l.5 8.5h5l.5-8.5"/></svg><span class="mi-label">Housekeeping</span></button> <button id="backlogOpenBtn" class="menu-item" style="display:none" onclick="closeMenus(); openBacklogModal()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 3.5h12v3H2zM3 6.5v6h10v-6M6.5 9h3"/></svg><span class="mi-label">Open backlog</span></button> <button id="backlogToggleBtn" class="menu-item" style="display:none" onclick="closeMenus(); toggleBacklogFile()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 2.5h7l3 3v8H3zM10 2.5v3h3M5.5 9h5M5.5 11.5h5"/></svg><span class="mi-label">Backlog file</span></button><div class="menu-sep"></div><button id="renameProjectBtn" class="menu-item" style="display:none" title="Rename project" onclick="closeMenus(); renameCurrentProject()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M10.5 2.5l3 3L5 14H2v-3z"/></svg><span class="mi-label">Rename project</span></button> <button id="deleteProjectBtn" class="menu-item danger" style="display:none" title="Delete project" onclick="closeMenus(); deleteCurrentProject()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M5 4.5l.5 8.5h5l.5-8.5"/></svg><span class="mi-label">Delete project</span></button><div class="menu-sep"></div><div class="menu-row"><span class="menu-label"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6"/><path d="M2 8h12M8 2c2 2 2 10 0 12M8 2c-2 2-2 10 0 12"/></svg><span id="languageLabel">Language</span></span><div class="dd dd-field dd-compact"><input type="hidden" id="languageSelector" value="en" onchange="setLanguage(this.value)"> <button type="button" class="dd-trigger" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="languageLabel"></button><div class="dd-menu" role="listbox"></div></div></div><div class="menu-sep"></div><a id="homeLink" class="menu-item" href="https://ashikahmad.github.io/Kanban.md/" target="_blank" rel="noopener" onclick="closeMenus()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M2.5 7.5 8 3l5.5 4.5M4 6.5V13h3V9.5h2V13h3V6.5"/></svg><span class="mi-label">Kanban.md website</span></a> <a id="sourceLink" class="menu-item" href="https://github.com/ashikahmad/Kanban.md" target="_blank" rel="noopener" onclick="closeMenus()"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M5.5 4.5 2 8l3.5 3.5M10.5 4.5 14 8l-3.5 3.5"/></svg><span class="mi-label">Source on GitHub</span></a></div></div><h1><svg class="app-logo" viewBox="0 0 64 64" aria-hidden="true"><rect x="0.75" y="0.75" width="62.5" height="62.5" rx="14.5" style="fill:var(--logo-tile);stroke:var(--logo-ring)" stroke-width="1.5"/><g style="stroke:var(--logo-hash)" stroke-width="2.4" stroke-linecap="round" fill="none"><path d="M15.4 12.8 14.4 26.4M20 12.8 19 26.4M12 17.2h10.8M11.6 22.4h10.8"/></g><rect x="26.5" y="12" width="11" height="40" rx="3" style="fill:var(--logo-tall)"/><rect x="42" y="12" width="11" height="28" rx="3" style="fill:var(--logo-short)"/></svg><span id="headerTitle">Kanban.md</span></h1><div class="dd dd-field dd-compact" id="projectDd" style="display:none"><input type="hidden" id="projectSelector"> <button type="button" class="dd-trigger" aria-haspopup="listbox" aria-expanded="false" aria-label="Project"></button><div class="dd-menu" role="listbox"></div></div><div id="filterBar"><div class="filter-row"><div class="search-field"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5 14 14"/></svg> <input type="text" id="globalSearchInput" placeholder="Search..." oninput="applyGlobalSearch()" aria-label="Search"> <button id="clearGlobalSearch" onclick="clearGlobalSearch()" title="Clear search" aria-label="Clear search">&times;</button></div><button class="btn btn-secondary" id="filterToggleBtn" onclick="toggleFilterPanel(event)" aria-haspopup="true" aria-expanded="false"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 3.5h12M4.5 8h7M7 12.5h2"/></svg><span id="filterToggleLabel">Filters</span><span class="filter-count" id="filterCount" style="display:none">0</span></button><div class="filter-group" id="filterPanel" onclick="event.stopPropagation()"><div class="fdrop dd" data-type="tag"><button class="fbtn dd-trigger" aria-haspopup="true" aria-expanded="false"><span class="flabel">Tags</span><span class="fcount"></span><svg class="ic chev" viewBox="0 0 16 16" aria-hidden="true"><path d="M4 6.5l4 4 4-4"/></svg></button><div class="fmenu dd-menu" id="fmenu-tag" role="group"></div></div><div class="fdrop dd" data-type="category"><button class="fbtn dd-trigger" aria-haspopup="true" aria-expanded="false"><span class="flabel">Category</span><span class="fcount"></span><svg class="ic chev" viewBox="0 0 16 16" aria-hidden="true"><path d="M4 6.5l4 4 4-4"/></svg></button><div class="fmenu dd-menu" id="fmenu-category" role="group"></div></div><div class="fdrop dd" data-type="user"><button class="fbtn dd-trigger" aria-haspopup="true" aria-expanded="false"><span class="flabel">User</span><span class="fcount"></span><svg class="ic chev" viewBox="0 0 16 16" aria-hidden="true"><path d="M4 6.5l4 4 4-4"/></svg></button><div class="fmenu dd-menu" id="fmenu-user" role="group"></div></div><div class="fdrop dd" data-type="priority"><button class="fbtn dd-trigger" aria-haspopup="true" aria-expanded="false"><span class="flabel">Priority</span><span class="fcount"></span><svg class="ic chev" viewBox="0 0 16 16" aria-hidden="true"><path d="M4 6.5l4 4 4-4"/></svg></button><div class="fmenu dd-menu" id="fmenu-priority" role="group"></div></div></div></div></div><div class="header-spacer"></div><button id="newTaskBtn" class="btn btn-primary" style="display:none">Task</button><div class="menu-wrap"><button class="btn btn-ghost btn-icon" id="themeMenuBtn" onclick="toggleThemeMenu(event)" aria-haspopup="true" aria-expanded="false" title="Theme" aria-label="Theme"></button><div class="popover" id="themeMenu" onclick="event.stopPropagation()"><button class="menu-item" data-theme-mode="auto" onclick="setThemeMode('auto')"><span class="check"></span><span class="lbl">Auto</span></button> <button class="menu-item" data-theme-mode="light" onclick="setThemeMode('light')"><span class="check"></span><span class="lbl">Light</span></button> <button class="menu-item" data-theme-mode="dark" onclick="setThemeMode('dark')"><span class="check"></span><span class="lbl">Dark</span></button></div></div></header><div id="activeFilters"></div><div id="housekeepingBar" class="hk-bar" style="display:none" role="status"></div><input type="file" id="pickFolderInput" webkitdirectory multiple="multiple" hidden aria-hidden="true" tabindex="-1"><div class="container"><div id="welcomeScreen" class="welcome"></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">Task details</h2><button class="close-btn" onclick="closeModal()" aria-label="Close">&times;</button></div><div id="modalBody" class="task-detail"></div><div id="taskModalActions" class="actions"></div></div></div><div id="shareModal" class="modal"><div class="modal-content"><div class="modal-header"><h2 id="shareModalTitle">Share task</h2><button class="close-btn" onclick="closeShareDialog()" aria-label="Close">&times;</button></div><div class="modal-body"><div class="share-row"><button type="button" id="shareFormatText" class="btn btn-primary btn-sm" onclick="setShareOption('format', 'text')">Plain text</button> <button type="button" id="shareFormatMd" class="btn btn-secondary btn-sm" onclick="setShareOption('format', 'markdown')">Markdown</button> <span class="share-view" id="shareView" role="group" style="margin-left:auto;display:none"><button type="button" id="shareViewRaw" class="btn btn-sm" onclick="setShareOption('view', 'raw')" aria-label="Raw Markdown" title="Raw Markdown"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg></button> <button type="button" id="shareViewRendered" class="btn btn-sm" onclick="setShareOption('view', 'rendered')" aria-label="Preview" title="Preview"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg></button></span></div><div id="shareRendered" class="share-rendered" style="display:none"></div><textarea id="sharePreview" class="share-preview" readonly="readonly" rows="12" spellcheck="false"></textarea> <label id="shareNotesRow" class="share-opt"><input type="checkbox" id="shareNotes" onchange="setShareOption('notes', this.checked)"> <span id="shareNotesLabel">Include notes</span></label> <label id="shareAssigneesRow" class="share-opt"><input type="checkbox" id="shareAssignees" onchange="setShareOption('assignees', this.checked)"> <span id="shareAssigneesLabel">Include assignees</span></label><p id="shareHint" class="share-hint"></p></div><div class="actions"><button type="button" id="shareCopyBtn" class="btn btn-primary" onclick="copyShare()">Copy</button> <button type="button" id="shareNativeBtn" class="btn btn-secondary" onclick="nativeShare()" style="display:none">Share…</button></div></div></div><div id="newTaskModal" class="modal"><div class="modal-content"><div class="modal-header"><h2>New task</h2><button class="close-btn" onclick="closeTaskModal()" aria-label="Close">&times;</button></div><form id="newTaskForm" class="form-stack"><input type="hidden" id="taskEditId"><div class="field"><label for="taskTitle">Title *</label> <input type="text" id="taskTitle" required></div><div class="form-grid cols-2"><div class="field"><label id="taskStatusLabel" for="taskStatus">Column *</label><div class="dd dd-field"><input type="hidden" id="taskStatus"> <button type="button" class="dd-trigger" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="taskStatusLabel"></button><div class="dd-menu" role="listbox"></div></div></div><div class="field"><label id="taskPriorityLabel" for="taskPriority">Priority</label><div class="dd dd-field"><input type="hidden" id="taskPriority"> <button type="button" class="dd-trigger" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="taskPriorityLabel"></button><div class="dd-menu" role="listbox"></div></div></div></div><div class="form-grid cols-2"><div class="field"><label for="taskCategory">Category</label> <input type="text" id="taskCategory" list="categoriesList" placeholder="Frontend, Backend..."></div><div class="field"><label for="taskAssignee">Assigned to</label> <input type="text" id="taskAssignee" list="usersList" placeholder="@alice"></div></div><div class="form-grid cols-4"><div class="field"><label for="taskCreated">Created</label><input type="date" id="taskCreated"></div><div class="field"><label for="taskStarted">Started</label><input type="date" id="taskStarted"></div><div class="field"><label for="taskDue">Due</label><input type="date" id="taskDue"></div><div class="field"><label for="taskCompleted">Finished</label><input type="date" id="taskCompleted"></div></div><div class="field"><label for="taskTags">Tags</label> <input type="text" id="taskTags" list="tagsList" placeholder="#bug #feature"> <small>Separate with spaces</small></div><div class="field" id="taskBlockedByField"></div><div class="field"><label for="taskDescription">Description</label> <textarea id="taskDescription" rows="3"></textarea></div><div class="field"><label>Subtasks</label><ul id="formSubtasksList" class="subtask-list"></ul><div class="subtask-add"><input type="text" id="formSubtaskInput" placeholder="Add a subtask..."> <button type="button" onclick="addFormSubtask()" class="btn btn-secondary">+ Add</button></div></div><div class="field"><label for="taskNotes">Notes</label> <textarea id="taskNotes" rows="5"></textarea> <small>Markdown supported</small></div><div class="actions"><button type="button" class="btn btn-secondary" onclick="closeTaskModal()">Cancel</button> <button type="submit" class="btn btn-primary" id="taskFormSubmitBtn">Create</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 narrow" style="max-width:560px"><div class="modal-header"><h2 id="columnsModalTitle">Manage columns</h2><button class="close-btn" onclick="closeColumnsModal()" aria-label="Close">&times;</button></div><div class="modal-body"><div id="columnsList" class="list-rows"></div><button id="addColumnBtn" onclick="addColumn()" class="btn btn-secondary" style="margin-top:var(--s-3);width:100%">+ Add column</button></div></div></div><div id="archiveModal" class="modal"><div class="modal-content wide"><div class="modal-header"><h2 id="archiveModalTitle">Archives</h2><button class="close-btn" onclick="closeArchiveModal()" aria-label="Close">&times;</button></div><div class="modal-body"><input type="text" id="archiveSearch" placeholder="Search archives..." style="margin-bottom:var(--s-3)"><div id="archiveList" class="list-rows" onclick="onArchiveListClick(event)" style="max-height:500px;overflow-y:auto"></div></div></div></div><div id="backlogModal" class="modal"><div class="modal-content wide"><div class="modal-header"><h2 id="backlogModalTitle">Backlog</h2><button class="close-btn" onclick="closeBacklogModal()" aria-label="Close">&times;</button></div><div class="modal-body"><input type="text" id="backlogSearch" placeholder="Search backlog..." style="margin-bottom:var(--s-3)"><div id="backlogList" class="list-rows" onclick="onBacklogListClick(event)" style="max-height:500px;overflow-y:auto"></div></div></div></div><footer id="statusBar" class="status-bar" style="display:none"></footer><div id="pickNoteModal" class="modal" role="dialog" aria-modal="true" aria-labelledby="pickNoteTitle"><div class="modal-content" style="max-width:440px"><div class="modal-header"><h2 id="pickNoteTitle">Opening a folder in this browser</h2><button class="close-btn" onclick="closePickNote()" aria-label="Close">&times;</button></div><div class="modal-body"><p id="pickNoteText"></p><p id="pickNoteServe" class="note-serve" style="display:none"><span id="pickNoteServeText"></span> <a href="#" id="pickNoteServeLink" onclick="event.preventDefault(); closePickNote(); openServeHelp()"></a></p></div><div class="actions"><button class="btn btn-secondary" id="pickNoteCancel" onclick="closePickNote()">Cancel</button> <button class="btn btn-primary" id="pickNoteContinue" onclick="continuePickNote()">Continue</button></div></div></div><div id="serveHelpModal" class="modal" role="dialog" aria-modal="true" aria-labelledby="serveHelpTitle"><div class="modal-content" style="max-width:520px"><div class="modal-header"><h2 id="serveHelpTitle">Edit this folder in your browser</h2><button class="close-btn" onclick="closeServeHelp()" aria-label="Close">&times;</button></div><div class="modal-body"><p id="serveHelpIntro"></p><ol class="steps"><li id="serveHelpStep1"></li><li><span id="serveHelpStep2"></span><div class="cmd-row"><code id="serveHelpCmd">kanbanmd serve</code><button class="btn btn-secondary btn-sm" id="serveHelpCopy" onclick="copyServeCommand()">Copy</button></div></li><li id="serveHelpStep3"></li></ol></div><div class="actions"><button class="btn btn-primary" id="serveHelpClose" onclick="closeServeHelp()">Close</button></div></div></div><div id="notification" class="notification"><span id="notificationText"></span></div><script>var GfmRender=(()=>{var e=Object.defineProperty,t=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,s=e=>{throw TypeError(e)},r=(t,n,a)=>((t,n,a)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[n]=a)(t,"symbol"!=typeof n?n+"":n,a),o=(e,t,n)=>(((e,t,n)=>{t.has(e)||s("Cannot "+n)})(e,t,"access private method"),n),i={};function l(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}((t,n)=>{for(var a in n)e(t,a,{get:n[a],enumerable:!0})})(i,{renderGfm:()=>Ye});var c={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};function d(e){c=e}var u={exec:()=>null};function p(e){let t=[];return n=>{let a=Math.max(0,Math.min(3,n-1)),s=t[a];return s||(s=e(a),t[a]=s),s}}function h(e,t=""){let n="string"==typeof e?e:e.source,a={replace:(e,t)=>{let s="string"==typeof t?t:t.source;return s=s.replace(g.caret,"$1"),n=n.replace(e,s),a},getRegex:()=>new RegExp(n,t)};return a}var m=((e="")=>{try{return!!new RegExp("(?<=1)(?<!1)"+e)}catch{return!1}})(),g={codeRemoveIndent:/^(?: {0,3}\t| {1,4})/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,endingSpaceTabChar:/[ \t]$/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,leadingSpaceTab:/^[ \t]+/,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^<a /i,endATag:/^<\/a>/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^</,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,numericCharacterReference:/&#(?:(\d{1,7})|[Xx]([A-Fa-f0-9]{1,6}));/,numericCharacterReferenceOrAmpersand:/&#(?:(\d{1,7})|[Xx]([A-Fa-f0-9]{1,6}));|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[\t ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:p(e=>new RegExp(`^ {0,${e}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`)),hrRegex:p(e=>new RegExp(`^ {0,${e}}((?:-[ \t]*){3,}|(?:_[ \t]*){3,}|(?:\\*[ \t]*){3,})(?:\\n+|$)`)),fencesBeginRegex:p(e=>new RegExp(`^ {0,${e}}(?:\`\`\`|~~~)`)),headingBeginRegex:p(e=>new RegExp(`^ {0,${e}}#`)),htmlBeginRegex:p(e=>new RegExp(`^ {0,${e}}(?:</?(?:${T})(?: +|$|/?>)|<(?:(?:script|pre|style|textarea)(?:[\\s>]|$)|!--)|<\\?|<![A-Za-z]|<!\\[CDATA\\[)`,"i")),blockquoteBeginRegex:p(e=>new RegExp(`^ {0,${e}}>`))},k=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,f=/ {0,3}(?:[*+-]|\d{1,9}[.)])/,b=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |fences|blockquote|heading|hr|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,v=h(b).replace(/bull/g,f).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}(?:\s|$)/).replace(/hr/g,/ {0,3}(?:(?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),y=h(b).replace(/bull/g,f).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}(?:\s|$)/).replace(/hr/g,/ {0,3}(?:(?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),w=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table|[ \t]+\n)[^\n]+)*)/,S=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,x=h(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",S).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),B=h(/^(bull)([ \t][^\n]*?)?(?:\n|$)/).replace(/bull/g,f).getRegex(),T="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",$=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,E=h("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n*|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>[^\\n]*\\n*|$)|<![A-Z][\\s\\S]*?(?:>[^\\n]*\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>[^\\n]*\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!(?:script|pre|style|textarea)(?![a-z0-9-]))([a-z][a-z0-9-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|</[a-z][a-z0-9-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))","i").replace("comment",$).replace("tag",T).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),C=e=>h(w).replace("hr",k).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list",e).replace("html"," {0,3}(?:</?(?:tag)(?: +|\\n|/?>)|<(?:(?:script|pre|style|textarea)(?:[\\s>]|$)|!--)|<\\?|<![A-Za-z]|<!\\[CDATA\\[)").replace("tag",T).getRegex(),A=C(/ {0,3}(?:[*+-]|1[.)])[ \t]+[^ \t\n]/),I=C(/ {0,3}(?:[*+-]|\d{1,9}[.)])(?:[ \t]|\n|$)/),F={blockquote:h(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",I).getRegex(),code:/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,def:x,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:k,html:E,lheading:v,list:B,newline:/^(?:[ \t]*(?:\n|$))+/,paragraph:A,table:u,text:/^[^\n]+/},L=h("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",k).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3}\t)[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html"," {0,3}(?:</?(?:tag)(?: +|\\n|/?>)|<(?:(?:script|pre|style|textarea)(?:[\\s>]|$)|!--)|<\\?|<![A-Za-z]|<!\\[CDATA\\[)").replace("tag",T).getRegex(),D={...F,lheading:y,table:L,paragraph:h(w).replace("hr",k).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",L).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]").replace("html"," {0,3}(?:</?(?:tag)(?: +|\\n|/?>)|<(?:(?:script|pre|style|textarea)(?:[\\s>]|$)|!--)|<\\?|<![A-Za-z]|<!\\[CDATA\\[)").replace("tag",T).getRegex()},O={...F,html:h("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:\"[^\"]*\"|'[^']*'|\\s[^'\"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",$).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:u,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:h(w).replace("hr",k).replace("heading"," *#{1,6} *[^\n]").replace("lheading",v).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},P=/^( {2,}|\\)\n(?!\s*$)[ \t]*/,M=/[\p{P}\p{S}]/u,H=/[\s\p{P}\p{S}]/u,R=/[^\s\p{P}\p{S}]/u,N=h(/^((?![*_])punctSpace)/,"u").replace(/punctSpace/g,H).getRegex(),_=/(?!~)[\p{P}\p{S}]/u,j=h(/link|precode-code|html/,"g").replace("link",/\[(?:[^\[\]`]|(?<a>`+)[^`]+\k<a>(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",m?"(?<!`)()":"(^^|[^`])").replace("code",/(?<b>`+)[^`]+\k<b>(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),z=/^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/,K=h(z,"u").replace(/punct/g,M).getRegex(),q=h(z,"u").replace(/punct/g,_).getRegex(),U=h(/^(?:\*+(?:((?!\*)(?!openQuote)punct)|([^\s*]))?)|^_+(?:((?!_)(?!openQuote)punct)|([^\s_]))?/,"u").replace(/openQuote/g,/[\p{Pi}\p{Ps}"']/u).replace(/punct/g,M).getRegex(),V="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",W=h(V,"gu").replace(/notPunctSpace/g,R).replace(/punctSpace/g,H).replace(/punct/g,M).getRegex(),G=h(V,"gu").replace(/notPunctSpace/g,/(?:[^\s\p{P}\p{S}]|~)/u).replace(/punctSpace/g,/(?!~)[\s\p{P}\p{S}]/u).replace(/punct/g,_).getRegex(),Z=h("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)[\\s](\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|(?:(?!\\*)punct|notPunctSpace)(\\*+)(?!\\*)(?=notPunctSpace)","gu").replace(/notPunctSpace/g,R).replace(/punctSpace/g,H).replace(/punct/g,M).getRegex(),Y=h("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,R).replace(/punctSpace/g,H).replace(/punct/g,M).getRegex(),Q=h("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)[\\s](_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)|(?:(?!_)punct|notPunctSpace)(_+)(?!_)(?=notPunctSpace)","gu").replace(/notPunctSpace/g,R).replace(/punctSpace/g,H).replace(/punct/g,M).getRegex(),X=h(/^~~?(?:((?!~)punct)|[^\s~])/,"u").replace(/punct/g,M).getRegex(),J=h("^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)","gu").replace(/notPunctSpace/g,R).replace(/punctSpace/g,H).replace(/punct/g,M).getRegex(),ee=h(/\\(punct)/,"gu").replace(/punct/g,M).getRegex(),te=h(/(`+)(?:[^`]|[^`][\s\S]*?[^`])\1(?!`)|`+|\\(punct)/,"gu").replace(/punct/g,M).getRegex(),ne=h(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),ae=h($).replace("(?:--\x3e|$)","--\x3e").getRegex(),se=h("^comment|^</[a-zA-Z][a-zA-Z0-9-]*\\s*>|^<[a-zA-Z][a-zA-Z0-9-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment",ae).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),re=/\[(?:\\[\s\S]|[^\[\]\\])*\]/,oe=h(/(?:\[(?:brackets|\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\])|[^\[\]\\`])*?/).replace("brackets",re).getRegex(),ie=h(/^!?\[(label)\]\([ \t\n]*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?[ \t\n]*\)/).replace("label",oe).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]+|(?=\))/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),le=h(/^!?\[(label)\]\[(ref)\]/).replace("label",oe).replace("ref",S).getRegex(),ce=h(/^!?\[(ref)\](?:\[\])?/).replace("ref",S).getRegex(),de=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\]){1,999}/,ue=h(/(?:[^\[\]\\`]*(?:\[(?:brackets|\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\]))){0,999}?[^\[\]\\`]*?/).replace("brackets",re).getRegex(),pe=h("reflink|nolink(?!\\()","g").replace("reflink",h(/^!?\[(label)\]\[(ref)\]/).replace("label",ue).replace("ref",de).getRegex()).replace("nolink",h(/^!?\[(ref)\](?:\[\])?/).replace("ref",de).getRegex()).getRegex(),he=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,me=h(/(?:mailto:email|xmpp:email(?:\/[A-Za-z0-9@.]+)?)/).replace(/email/g,/[A-Za-z0-9._+-]+@[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![\w-])/).getRegex(),ge={_backpedal:u,anyPunctuation:ee,anyPunctuationOutsideCode:te,autolink:ne,blockSkip:j,br:P,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:u,delLDelim:u,delRDelim:u,emStrongLDelim:K,emStrongRDelimAst:W,emStrongRDelimUnd:Y,escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,link:ie,nolink:ce,punctuation:N,reflink:le,reflinkSearch:pe,tag:se,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,url:u},ke={...ge,emStrongLDelim:U,emStrongRDelimAst:Z,emStrongRDelimUnd:Q,link:h(/^!?\[(label)\]\((.*?)\)/).replace("label",oe).getRegex(),reflink:h(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",oe).getRegex()},fe={...ge,emStrongRDelimAst:G,emStrongLDelim:q,delLDelim:X,delRDelim:J,url:h(/^emailProtocol|^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("emailProtocol",me).replace("protocol",he).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![\w-])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:h(/^(?:[^a-zA-Z0-9](?=emailProtocol)|(`+|~+|[^`~])(?:(?=[`~])|(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|protocol:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9](?=emailProtocol)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))))/).replace("protocol",he).replace(/emailProtocol/g,/(?:mailto|xmpp):/).getRegex()},be={...fe,br:h(P).replace("{2,}","*").getRegex(),text:h(fe.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()},ve={normal:F,gfm:D,pedantic:O},ye={normal:ge,gfm:fe,breaks:be,pedantic:ke},we={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},Se=e=>we[e];function xe(e,t){if(t){if(g.escapeTest.test(e))return e.replace(g.escapeReplace,Se)}else if(g.escapeTestNoEncode.test(e))return e.replace(g.escapeReplaceNoEncode,Se);return e}function Be(e){return g.numericCharacterReference.test(e)?e.replace(g.numericCharacterReferenceOrAmpersand,(e,t,n)=>{if(void 0===t&&void 0===n)return"&amp;";let a=void 0===t?Number.parseInt(n,16):Number.parseInt(t,10);return 0===a||a>1114111||a>=55296&&a<=57343?"�":38===a?"&amp;":String.fromCodePoint(a)}):e}function Te(e){try{e=encodeURI(e).replace(g.percentDecode,"%")}catch{return null}return e}function $e(e,t){let n=e.replace(g.findPipe,(e,t,n)=>{let a=!1,s=t;for(;--s>=0&&"\\"===n[s];)a=!a;return a?"|":" |"}).split(g.splitPipe),a=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;a<n.length;a++)n[a]=n[a].trim().replace(g.slashPipe,"|");return n}function Ee(e,t,n){let a=e.length;if(0===a)return"";let s=0;for(;s<a;){let r=e.charAt(a-s-1);if(r!==t||n){if(r===t||!n)break;s++}else s++}return e.slice(0,a-s)}function Ce(e){let t=e.split("\n"),n=t.length-1;for(;n>=0&&g.blankLine.test(t[n]);)n--;return t.length-n<=2?e:t.slice(0,n+1).join("\n")}function Ae(e){return e.trim().toLowerCase().toUpperCase().toLowerCase()}function Ie(e,t){if(-1===e.indexOf(t[0])&&-1===e.indexOf(t[1]))return-1;let n=0;for(let a=0;a<e.length;a++)if("\\"===e[a])a++;else if(e[a]===t[0])n++;else if(e[a]===t[1]&&(n--,n<0))return a;return n>0?-2:-1}function Fe(e,t=0){let n=t,a="";for(let t of e)if("\t"===t){let e=4-n%4;a+=" ".repeat(e),n+=e}else a+=t,n++;return a}function Le(e,t,n,a,s){let r=t.href,o=t.title||null,i=e[1].replace(s.other.outputLinkReplace,"$1"),l="!"===e[0].charAt(0),c=a.state.inLink,d=a.state.linkEmitted,u=a.state.inRawBlock;a.state.inLink=!0,a.state.linkEmitted=!1;let p=a.inlineTokens(i),h=a.state.linkEmitted;if(a.state.linkEmitted=d,a.state.inLink=c,!l){if(h)return void(a.state.inRawBlock=u);a.state.linkEmitted=!0}return{type:l?"image":"link",raw:n,href:r,title:o,text:i,tokens:p}}function De(e,t,n,a){if(!t.includes("<"))return!1;for(let s=0;s<t.length;s++){if("\\"===t[s]){s++;continue}if("`"===t[s]){let e=a.inline.code.exec(t.slice(s));if(e){s+=e[0].length-1;continue}}if("<"!==t[s])continue;let r=e.slice(n+s),o=a.inline.tag.exec(r)||a.inline.autolink.exec(r);if(o){if(o[0].length>t.length-s)return!0;s+=o[0].length-1}}return!1}var Oe,Pe,Me,He,Re=class{constructor(e){r(this,"options"),r(this,"rules"),r(this,"lexer"),this.options=e||c}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let e=this.options.pedantic?t[0]:Ce(t[0]),n=e.replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:e,codeBlockStyle:"indented",text:n}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let e=t[0],n=function(e,t,n){let a=e.match(n.other.indentCodeCompensation);if(null===a)return t;let s=a[1];return t.split("\n").map(e=>{let t=e.match(n.other.beginningSpace);if(null===t)return e;let[a]=t;return e.slice(Math.min(a.length,s.length))}).join("\n")}(e,t[3]||"",this.rules);return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(this.rules.other.endingHash.test(e)){let t=Ee(e,"#");(this.options.pedantic||!t||this.rules.other.endingSpaceTabChar.test(t))&&(e=t.trim())}return{type:"heading",raw:Ee(t[0],"\n"),depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:Ee(t[0],"\n")}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let e=Ee(t[0],"\n").split("\n"),n="",a="",s=[];for(;e.length>0;){let t,r=!1,o=[];for(t=0;t<e.length;t++)if(this.rules.other.blockquoteStart.test(e[t]))o.push(e[t]),r=!0;else{if(r)break;o.push(e[t])}e=e.slice(t);let i=o.join("\n"),l=i.replace(this.rules.other.blockquoteSetextReplace,"\n $1").replace(this.rules.other.blockquoteSetextReplace2,"");n=n?`${n}\n${i}`:i,a=a?`${a}\n${l}`:l;let c=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTokens(l,s,!0),this.lexer.state.top=c,0===e.length)break;let d=s.at(-1);if("code"===d?.type||"heading"===d?.type||"hr"===d?.type||"html"===d?.type)break;if("blockquote"===d?.type){let t=d,r=e.join("\n"),o=t.raw+"\n"+r.replace(this.rules.other.blockquoteSetextReplace2,""),i=this.blockquote(o);s[s.length-1]=i;let l=o.substring(i.raw.length).replace(/^\n/,""),c=l?l.split("\n").length:0,u=c?e.slice(0,-c):e;u.length>0&&(n=`${n}\n${u.join("\n")}`),a=a.substring(0,a.length-t.text.length)+i.text;break}if("list"===d?.type){let t=d,r=t.raw+"\n"+e.join("\n"),o=this.list(r);s[s.length-1]=o,n=n.substring(0,n.length-d.raw.length)+o.raw,a=a.substring(0,a.length-t.raw.length)+o.raw,e=r.substring(s.at(-1).raw.length).split("\n");continue}}return{type:"blockquote",raw:n,tokens:s,text:a}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=e,a=()=>n.length-e.length,s=t[1].trim(),r=s.length>1,o={type:"list",raw:"",ordered:r,start:r?+s.slice(0,-1):"",loose:!1,items:[]};s=r?`\\d{1,9}\\${s.slice(-1)}`:`\\${s}`,this.options.pedantic&&(s=r?s:"[*+-]");let i=this.rules.other.listItemRegex(s),l=!1;for(;e;){let s=!1,r="";if(!(t=i.exec(e))||this.rules.block.hr.test(e))break;let c=a();e=e.substring(t[0].length);let d=t[2].split("\n",1)[0],u=t[1].length,p=this.options.pedantic?Fe(d,u):d.replace(this.rules.other.leadingSpaceTab,e=>Fe(e,u)),h=e.split("\n",1)[0],m=!p.trim(),g=0;if(this.options.pedantic?(g=2,r=p.trimStart()):m?g=u+1:(g=p.search(this.rules.other.nonSpaceChar),g=g>4?1:g,r=p.slice(g),g+=u),m&&this.rules.other.blankLine.test(h)&&(e=e.substring(h.length+1),s=!0),!s){let t=this.rules.other.nextBulletRegex(g),n=this.rules.other.hrRegex(g),a=this.rules.other.fencesBeginRegex(g),s=this.rules.other.headingBeginRegex(g),o=this.rules.other.htmlBeginRegex(g),i=this.rules.other.blockquoteBeginRegex(g);for(;e;){let l,c=e.split("\n",1)[0];if(h=c,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting," "),l=h):l=h.replace(this.rules.other.leadingSpaceTab,e=>Fe(e)),a.test(h)||s.test(h)||o.test(h)||i.test(h)||t.test(h)||n.test(h))break;if(l.search(this.rules.other.nonSpaceChar)>=g||!h.trim())r+="\n"+l.slice(g);else{if(m||p.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||a.test(p)||s.test(p)||n.test(p))break;r+="\n"+h}m=!h.trim(),e=e.substring(c.length+1),p=l.slice(g)}}let k=n.slice(c,a());o.loose||(l?o.loose=!0:this.rules.other.doubleBlankLine.test(k)&&(l=!0)),o.items.push({type:"list_item",raw:k,task:!!this.options.gfm&&this.rules.other.listIsTask.test(r),loose:!1,text:r,tokens:[]})}let c=o.items.at(-1);if(!c)return;c.raw=c.raw.trimEnd(),c.text=c.text.trimEnd(),o.raw=n.slice(0,a()).trimEnd();for(let e of o.items)if(this.lexer.state.top=!1,e.tokens=this.lexer.blockTokens(e.text,[]),!o.loose){let t=e.tokens.filter(e=>"space"===e.type),n=t.length>0&&t.some(e=>this.rules.other.anyLine.test(e.raw));o.loose=n}for(let e of o.items){let t=e.tokens[0];if(!e.task||"text"!==t?.type&&"paragraph"!==t?.type)e.task&&(e.task=!1);else{e.text=e.text.replace(this.rules.other.listReplaceTask,""),t.raw=t.raw.replace(this.rules.other.listReplaceTask,""),t.text=t.text.replace(this.rules.other.listReplaceTask,"");for(let e=this.lexer.inlineQueue.length-1;e>=0;e--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[e].src)){this.lexer.inlineQueue[e].src=this.lexer.inlineQueue[e].src.replace(this.rules.other.listReplaceTask,"");break}let n=this.rules.other.listTaskCheckbox.exec(e.raw);if(n){let t={type:"checkbox",raw:n[0]+" ",checked:"[ ]"!==n[0]};e.checked=t.checked,o.loose?e.tokens[0]&&["paragraph","text"].includes(e.tokens[0].type)&&"tokens"in e.tokens[0]&&e.tokens[0].tokens?(e.tokens[0].raw=t.raw+e.tokens[0].raw,e.tokens[0].text=t.raw+e.tokens[0].text,e.tokens[0].tokens.unshift(t)):e.tokens.unshift({type:"paragraph",raw:t.raw,text:t.raw,tokens:[t]}):e.tokens.unshift(t)}}}if(o.loose)for(let e of o.items){e.loose=!0;for(let t of e.tokens)"text"===t.type&&(t.type="paragraph")}return o}}html(e){let t=this.rules.block.html.exec(e);if(t){let e=Ce(t[0]);return{type:"html",block:!0,raw:e,pre:"pre"===t[1]||"script"===t[1]||"style"===t[1],text:e}}}def(e){let t=this.rules.block.def.exec(e);if(t){if(!this.rules.other.startAngleBracket.test(t[2])&&-1!==Ie(t[2],"()"))return;let e=Ae(t[1]).replace(this.rules.other.multipleSpaceGlobal," "),n=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",a=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:Ee(t[0],"\n"),href:n,title:a}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=$e(t[1]),a=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),s=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split("\n"):[],r={type:"table",raw:Ee(t[0],"\n"),header:[],align:[],rows:[]};if(n.length===a.length){for(let e of a)this.rules.other.tableAlignRight.test(e)?r.align.push("right"):this.rules.other.tableAlignCenter.test(e)?r.align.push("center"):this.rules.other.tableAlignLeft.test(e)?r.align.push("left"):r.align.push(null);for(let e=0;e<n.length;e++)r.header.push({text:n[e],tokens:this.lexer.inline(n[e]),header:!0,align:r.align[e]});for(let e of s)r.rows.push($e(e,r.header.length).map((e,t)=>({text:e,tokens:this.lexer.inline(e),header:!1,align:r.align[t]})));return r}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t){let e=t[1].trim();return{type:"heading",raw:Ee(t[0],"\n"),depth:"="===t[2].charAt(0)?1:2,text:e,tokens:this.lexer.inline(e)}}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){if(!1===this.lexer.state.linkParenPossible)return;let t=this.rules.inline.link.exec(e);if(t){let n="!"===t[0].charAt(0)?2:1;if(!this.options.pedantic&&De(e,t[1],n,this.rules))return;let a=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(a)){if(!this.rules.other.endAngleBracket.test(a))return;let e=Ee(a.slice(0,-1),"\\");if((a.length-e.length)%2==0)return}else{let e=Ie(t[2],"()");if(-2===e)return;if(e>-1){let n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let s=t[2],r="";if(this.options.pedantic){let e=this.rules.other.pedanticHrefTitle.exec(s);e&&(s=e[1],r=e[3])}else r=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(s=this.options.pedantic&&!this.rules.other.endAngleBracket.test(a)?s.slice(1):s.slice(1,-1)),Le(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:r&&r.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let a="!"===n[0].charAt(0)?2:1;if(!this.options.pedantic&&De(e,n[1],a,this.rules))return;let s=t[Ae((n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "))];if(!s){let e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return Le(n,s,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let a=this.rules.inline.emStrongLDelim.exec(e);if(a&&(a[1]||a[2]||a[3]||a[4])&&(!a[4]||!n.match(this.rules.other.unicodeAlphaNumeric))&&(!a[1]&&!a[3]||!n||this.rules.inline.punctuation.exec(n))){let s,r,o=[...a[0]].length-1,i=o,l=0,c=a[0][0],d=n===c,u="*"===c?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(u.lastIndex=0,t=t.slice(-1*e.length+o);null!==(a=u.exec(t));){if(s=a[1]||a[2]||a[3]||a[4]||a[5]||a[6],!s)continue;if(r=[...s].length,a[3]||a[4]){i+=r;continue}if(a[5]||a[6]){if(o%3&&!((o+r)%3)){l+=r;continue}if(d)break}if(i-=r,i>0)continue;r=Math.min(r,r+i+l);let t=[...a[0]][0].length,n=e.slice(0,o+a.index+t+r);if(Math.min(o,r)%2){let e=n.slice(1,-1);return{type:"em",raw:n,text:e,tokens:this.lexer.inlineTokens(e)}}let c=n.slice(2,-2);return{type:"strong",raw:n,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(this.rules.other.newLineCharGlobal," "),n=this.rules.other.nonSpaceChar.test(e),a=this.rules.other.startingSpaceChar.test(e)&&this.rules.other.endingSpaceChar.test(e);return n&&a&&(e=e.substring(1,e.length-1)),{type:"codespan",raw:t[0],text:e}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e,t,n=""){let a=this.rules.inline.delLDelim.exec(e);if(a&&(!a[1]||!n||this.rules.inline.punctuation.exec(n))){let n,s,r=[...a[0]].length-1,o=r,i=this.rules.inline.delRDelim;for(i.lastIndex=0,t=t.slice(-1*e.length+r);null!==(a=i.exec(t));){if(n=a[1]||a[2]||a[3]||a[4]||a[5]||a[6],!n||(s=[...n].length,s!==r))continue;if(a[3]||a[4]){o+=s;continue}if(o-=s,o>0)continue;s=Math.min(s,s+o);let t=[...a[0]][0].length,i=e.slice(0,r+a.index+t+s),l=i.slice(r,-r);return{type:"del",raw:i,text:l,tokens:this.lexer.inlineTokens(l)}}}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=t[1],n="mailto:"+e):(e=t[1],n=e),{type:"link",raw:t[0],text:e,href:n,autolink:!0,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=t[0],n="mailto:"+e;else{let a;do{a=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(a!==t[0]);e=t[0],n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,autolink:!0,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let e=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:e?t[0]:Be(t[0]),escaped:e}}}},Ne=(Me=class{constructor(e){((e,t,n)=>{t.has(e)?s("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n)})(this,Oe),r(this,"tokens"),r(this,"options"),r(this,"state"),r(this,"inlineQueue"),r(this,"tokenizer"),this.tokens=[],this.tokens.links=Object.create(null),this.options=e||c,this.options.tokenizer=this.options.tokenizer||new Re,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,linkEmitted:!1,linkParenPossible:!0,top:!0};let t={other:g,block:ve.normal,inline:ye.normal};this.options.pedantic?(t.block=ve.pedantic,t.inline=ye.pedantic):this.options.gfm&&(t.block=ve.gfm,this.options.breaks?t.inline=ye.breaks:t.inline=ye.gfm),this.tokenizer.rules=t}static get rules(){return{block:ve,inline:ye}}static lex(e,t){return new Me(t).lex(e)}static lexInline(e,t){return new Me(t).inlineTokens(e)}lex(e){e=e.replace(g.carriageReturn,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e<this.inlineQueue.length;e++){let t=this.inlineQueue[e];this.inlineTokens(t.src,t.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[],n=!1){this.tokenizer.lexer=this,this.options.pedantic&&(e=e.replace(g.tabCharGlobal," ").replace(g.spaceLine,""));let a=1/0;for(;e;){if(!(e.length<a)){this.infiniteLoopError(e.charCodeAt(0));break}let s;if(a=e.length,this.options.extensions?.block?.some(n=>!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0)))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);let n=t.at(-1);1===s.raw.length&&void 0!==n?n.raw+="\n":t.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);let n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+=(n.raw.endsWith("\n")?"":"\n")+s.raw,n.text+="\n"+s.text,this.inlineQueue.at(-1).src=n.text):t.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);let n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+=(n.raw.endsWith("\n")?"":"\n")+s.raw,n.text+="\n"+s.raw,this.inlineQueue.at(-1).src=n.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title},t.push(s));continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),t.push(s);continue}let r=e;if(this.options.extensions?.startBlock){let t,n=1/0,a=e.slice(1);this.options.extensions.startBlock.forEach(e=>{t=e.call({lexer:this},a),"number"==typeof t&&t>=0&&(n=Math.min(n,t))}),n<1/0&&n>=0&&(r=e.substring(0,n+1))}if(this.state.top&&(s=this.tokenizer.paragraph(r))){let a=t.at(-1);n&&"paragraph"===a?.type?(a.raw+=(a.raw.endsWith("\n")?"":"\n")+s.raw,a.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=a.text):t.push(s),n=r.length!==e.length,e=e.substring(s.raw.length);continue}if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);let n=t.at(-1);"text"===n?.type?(n.raw+=(n.raw.endsWith("\n")?"":"\n")+s.raw,n.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=n.text):t.push(s);continue}if(e){this.infiniteLoopError(e.charCodeAt(0));break}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}linkInText(e){if(!e.includes("["))return!1;let t=this.tokenizer.rules.inline.link;for(let n of e.matchAll(this.tokenizer.rules.inline.blockSkip))if(t.test(n[0])&&"!"!==e.charAt(n.index-1))return!0;for(let t of e.matchAll(this.tokenizer.rules.inline.reflinkSearch)){let e=t[0],n=e.lastIndexOf("[");if("!"!==e.charAt(0)&&Object.hasOwn(this.tokens.links,Ae(e.slice(n+1,-1)))&&!(n>1&&this.linkInText(e.slice(1,n-1))))return!0}return!1}inlineTokens(e,t=[]){this.tokenizer.lexer=this;let n=this.state.linkParenPossible;this.state.linkParenPossible=n&&e.includes(")");try{return o(this,Oe,Pe).call(this,e,t)}finally{this.state.linkParenPossible=n}}infiniteLoopError(e){let t="Infinite loop on byte: "+e;if(!this.options.silent)throw new Error(t);console.error(t)}},Oe=new WeakSet,Pe=function(e,t){let n=e;if(this.tokens.links&&e.includes("[")){let e=this.tokenizer.rules.inline.reflinkSearch,t=n=>{let a=n.lastIndexOf("[");if(!Object.hasOwn(this.tokens.links,Ae(n.slice(a+1,-1))))return n;if(a>1&&"!"!==n.charAt(0)){let s=n.slice(1,a-1);if(this.linkInText(s))return"["+s.replace(e,t)+"]["+"a".repeat(n.length-a-2)+"]"}return"["+"a".repeat(n.length-2)+"]"};n=n.replace(e,t)}n=n.replace(this.tokenizer.rules.inline.anyPunctuationOutsideCode,(e,t,n)=>n?"+".repeat(e.length):t?t+e.slice(t.length,-t.length).replace(/`/g,"+")+t:e),n=n.replace(this.tokenizer.rules.inline.blockSkip,(e,t,n)=>{let a=n?n.length:0;return e.slice(0,a)+"["+"a".repeat(e.length-a-2)+"]"}),n=this.options.hooks?.emStrongMask?.call({lexer:this},n)??n;let a=!1,s="",r=1/0;for(;e;){if(!(e.length<r)){this.infiniteLoopError(e.charCodeAt(0));break}let o;if(r=e.length,a||(s=""),a=!1,this.options.extensions?.inline?.some(n=>!!(o=n.call({lexer:this},e,t))&&(e=e.substring(o.raw.length),t.push(o),!0)))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);let n=t.at(-1);"text"===o.type&&"text"===n?.type?(n.raw+=o.raw,n.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,s)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e,n,s)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let i=e;if(this.options.extensions?.startInline){let t,n=1/0,a=e.slice(1);this.options.extensions.startInline.forEach(e=>{t=e.call({lexer:this},a),"number"==typeof t&&t>=0&&(n=Math.min(n,t))}),n<1/0&&n>=0&&(i=e.substring(0,n+1))}if(o=this.tokenizer.inlineText(i)){e=e.substring(o.raw.length),"_"!==o.raw.slice(-1)&&(s=o.raw.slice(-1)),a=!0;let n=t.at(-1);"text"===n?.type?(n.raw+=o.raw,n.text+=o.text):t.push(o);continue}if(e){this.infiniteLoopError(e.charCodeAt(0));break}}return t},Me),_e=class{constructor(e){r(this,"options"),r(this,"parser"),this.options=e||c}space(e){return""}code({text:e,lang:t,escaped:n}){let a=(t||"").match(g.notSpaceStart)?.[0],s=e?e.replace(g.endingNewline,"")+"\n":"";return a?'<pre><code class="language-'+xe(a)+'">'+(n?s:xe(s,!0))+"</code></pre>\n":"<pre><code>"+(n?s:xe(s,!0))+"</code></pre>\n"}blockquote({tokens:e}){return`<blockquote>\n${this.parser.parse(e)}</blockquote>\n`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`<h${t}>${this.parser.parseInline(e)}</h${t}>\n`}hr(e){return"<hr>\n"}list(e){let t=e.ordered,n=e.start,a="";for(let t=0;t<e.items.length;t++){let n=e.items[t];a+=this.listitem(n)}let s=t?"ol":"ul";return"<"+s+(t&&1!==n?' start="'+n+'"':"")+">\n"+a+"</"+s+">\n"}listitem(e){return`<li>${this.parser.parse(e.tokens)}</li>\n`}checkbox({checked:e}){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox"> '}paragraph({tokens:e}){return`<p>${this.parser.parseInline(e)}</p>\n`}table(e){let t="",n="";for(let t=0;t<e.header.length;t++)n+=this.tablecell(e.header[t]);t+=this.tablerow({text:n});let a="";for(let t=0;t<e.rows.length;t++){let s=e.rows[t];n="";for(let e=0;e<s.length;e++)n+=this.tablecell(s[e]);a+=this.tablerow({text:n})}return a&&(a=`<tbody>${a}</tbody>`),"<table>\n<thead>\n"+t+"</thead>\n"+a+"</table>\n"}tablerow({text:e}){return`<tr>\n${e}</tr>\n`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`</${n}>\n`}strong({tokens:e}){return`<strong>${this.parser.parseInline(e)}</strong>`}em({tokens:e}){return`<em>${this.parser.parseInline(e)}</em>`}codespan({text:e}){return`<code>${xe(e,!0)}</code>`}br(e){return"<br>"}del({tokens:e}){return`<del>${this.parser.parseInline(e)}</del>`}link({href:e,title:t,text:n,tokens:a,autolink:s}){let r=s?xe(n,!0):this.parser.parseInline(a),o=Te(e);if(null===o)return r;let i='<a href="'+(e=xe(o,s))+'"';return t&&(i+=' title="'+xe(t)+'"'),i+=">"+r+"</a>",i}image({href:e,title:t,text:n,tokens:a}){a&&(n=this.parser.parseInline(a,this.parser.textRenderer));let s=Te(e);if(null===s)return xe(n);let r=`<img src="${xe(e=s)}" alt="${xe(n)}"`;return t&&(r+=` title="${xe(t)}"`),r+=">",r}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:xe(e.text)}},je=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}checkbox({raw:e}){return e}},ze=class e{constructor(e){r(this,"options"),r(this,"renderer"),r(this,"textRenderer"),this.options=e||c,this.options.renderer=this.options.renderer||new _e,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new je}static parse(t,n){return new e(n).parse(t)}static parseInline(t,n){return new e(n).parseInline(t)}parse(e){this.renderer.parser=this;let t="";for(let n=0;n<e.length;n++){let a=e[n];if(this.options.extensions?.renderers?.[a.type]){let e=a,n=this.options.extensions.renderers[e.type].call({parser:this},e);if(!1!==n||!["space","hr","heading","code","table","blockquote","list","checkbox","html","def","paragraph","text"].includes(e.type)){t+=n||"";continue}}let s=a;switch(s.type){case"space":t+=this.renderer.space(s);break;case"hr":t+=this.renderer.hr(s);break;case"heading":t+=this.renderer.heading(s);break;case"code":t+=this.renderer.code(s);break;case"table":t+=this.renderer.table(s);break;case"blockquote":t+=this.renderer.blockquote(s);break;case"list":t+=this.renderer.list(s);break;case"checkbox":t+=this.renderer.checkbox(s);break;case"html":t+=this.renderer.html(s);break;case"def":t+=this.renderer.def(s);break;case"paragraph":t+=this.renderer.paragraph(s);break;case"text":t+=this.renderer.text(s);break;default:{let e='Token with "'+s.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return t}parseInline(e,t=this.renderer){this.renderer.parser=this;let n="";for(let a=0;a<e.length;a++){let s=e[a];if(this.options.extensions?.renderers?.[s.type]){let e=this.options.extensions.renderers[s.type].call({parser:this},s);if(!1!==e||!["escape","html","link","image","checkbox","strong","em","codespan","br","del","text"].includes(s.type)){n+=e||"";continue}}let r=s;switch(r.type){case"escape":case"text":n+=t.text(r);break;case"html":n+=t.html(r);break;case"link":n+=t.link(r);break;case"image":n+=t.image(r);break;case"checkbox":n+=t.checkbox(r);break;case"strong":n+=t.strong(r);break;case"em":n+=t.em(r);break;case"codespan":n+=t.codespan(r);break;case"br":n+=t.br(r);break;case"del":n+=t.del(r);break;default:{let e='Token with "'+r.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}},Ke=(He=class{constructor(e){r(this,"options"),r(this,"block"),this.options=e||c}preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}emStrongMask(e){return e}provideLexer(e=this.block){return e?Ne.lex:Ne.lexInline}provideParser(e=this.block){return e?ze.parse:ze.parseInline}},r(He,"passThroughHooks",new Set(["preprocess","postprocess","processAllTokens","emStrongMask"])),r(He,"passThroughHooksRespectAsync",new Set(["preprocess","postprocess","processAllTokens"])),He);function qe(e,t){if(Array.isArray(t))for(let n=0;n<t.length;n++)e.push(t[n]);else e.push(t)}var Ue=class{constructor(...e){r(this,"defaults",{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}),r(this,"options",this.setOptions),r(this,"parse",this.parseMarkdown(!0)),r(this,"parseInline",this.parseMarkdown(!1)),r(this,"Parser",ze),r(this,"Renderer",_e),r(this,"TextRenderer",je),r(this,"Lexer",Ne),r(this,"Tokenizer",Re),r(this,"Hooks",Ke),this.use(...e)}walkTokens(e,t){let n=[];for(let a of e)switch(qe(n,t.call(this,a)),a.type){case"table":{let e=a;for(let a of e.header)qe(n,this.walkTokens(a.tokens,t));for(let a of e.rows)for(let e of a)qe(n,this.walkTokens(e.tokens,t));break}case"list":{let e=a;qe(n,this.walkTokens(e.items,t));break}default:{let e=a;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach(a=>{let s=e[a].flat(1/0);qe(n,this.walkTokens(s,t))}):e.tokens&&qe(n,this.walkTokens(e.tokens,t))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(e=>{let n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach(e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){let n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let a=e.renderer.apply(this,t);return!1===a&&(a=n.apply(this,t)),a}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");let n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)}),n.extensions=t),e.renderer){let t=this.defaults.renderer||new _e(this.defaults);for(let n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;let a=n,s=e.renderer[a],r=t[a];t[a]=(...e)=>{let n=s.apply(t,e);return!1===n&&(n=r.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){let t=this.defaults.tokenizer||new Re(this.defaults);for(let n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;let a=n,s=e.tokenizer[a],r=t[a];t[a]=(...e)=>{let n=s.apply(t,e);return!1===n&&(n=r.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){let t=this.defaults.hooks||new Ke;for(let n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;let a=n,s=e.hooks[a],r=t[a];Ke.passThroughHooks.has(n)?t[a]=e=>{if(this.defaults.async&&Ke.passThroughHooksRespectAsync.has(n))return(async()=>{let n=await s.call(t,e);return r.call(t,n)})();let a=s.call(t,e);return r.call(t,a)}:t[a]=(...e)=>{if(this.defaults.async)return(async()=>{let n=await s.apply(t,e);return!1===n&&(n=await r.apply(t,e)),n})();let n=s.apply(t,e);return!1===n&&(n=r.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){let t=this.defaults.walkTokens,a=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(a.call(this,e)),t&&qe(n,t.call(this,e)),n}}this.defaults={...this.defaults,...n}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return Ne.lex(e,t??this.defaults)}parser(e,t){return ze.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let a={...n},s={...this.defaults,...a},r=this.onError(!!s.silent,!!s.async);if(!0===this.defaults.async&&!1===a.async)return r(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof t>"u"||null===t)return r(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return r(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));if(s.hooks&&(s.hooks.options=s,s.hooks.block=e),s.async)return(async()=>{let n=s.hooks?await s.hooks.preprocess(t):t,a=await(s.hooks?await s.hooks.provideLexer(e):e?Ne.lex:Ne.lexInline)(n,s),r=s.hooks?await s.hooks.processAllTokens(a):a;s.walkTokens&&await Promise.all(this.walkTokens(r,s.walkTokens));let o=await(s.hooks?await s.hooks.provideParser(e):e?ze.parse:ze.parseInline)(r,s);return s.hooks?await s.hooks.postprocess(o):o})().catch(r);try{s.hooks&&(t=s.hooks.preprocess(t));let n=(s.hooks?s.hooks.provideLexer(e):e?Ne.lex:Ne.lexInline)(t,s);s.hooks&&(n=s.hooks.processAllTokens(n)),s.walkTokens&&this.walkTokens(n,s.walkTokens);let a=(s.hooks?s.hooks.provideParser(e):e?ze.parse:ze.parseInline)(n,s);return s.hooks&&(a=s.hooks.postprocess(a)),a}catch(e){return r(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){let e="<p>An error occurred:</p><pre>"+xe(n.message+"",!0)+"</pre>";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}},Ve=new Ue;function We(e,t){return Ve.parse(e,t)}We.options=We.setOptions=function(e){return Ve.setOptions(e),We.defaults=Ve.defaults,d(We.defaults),We},We.getDefaults=l,We.defaults=c,We.use=function(...e){return Ve.use(...e),We.defaults=Ve.defaults,d(We.defaults),We},We.walkTokens=function(e,t){return Ve.walkTokens(e,t)},We.parseInline=Ve.parseInline,We.Parser=ze,We.parser=ze.parse,We.Renderer=_e,We.TextRenderer=je,We.Lexer=Ne,We.lexer=Ne.lex,We.Tokenizer=Re,We.Hooks=Ke,We.parse=We;We.options,We.setOptions,We.walkTokens,We.parseInline,ze.parse,Ne.lex;var Ge=e=>String(e).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[e])),Ze=new Ue({gfm:!0,breaks:!1,renderer:{html:({text:e})=>Ge(e),image:({text:e})=>Ge(e),link({href:e,tokens:t}){let n=this.parser.parseInline(t);return/^(https?:|mailto:)/i.test(e)?`<a href="${Ge(e)}" target="_blank" rel="noopener noreferrer">${n}</a>`:n}}});function Ye(e){return Ze.parse(String(e??""),{async:!1})}return(s=>((s,r,o,i)=>{if(r&&"object"==typeof r||"function"==typeof r)for(let l of n(r))!a.call(s,l)&&l!==o&&e(s,l,{get:()=>r[l],enumerable:!(i=t(r,l))||i.enumerable});return s})(e({},"__esModule",{value:!0}),s))(i)})();const{renderGfm:renderGfm}=GfmRender;var KanbanCore=(()=>{var e=Object.defineProperty,t=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,s={};((t,n)=>{for(var a in n)e(t,a,{get:n[a],enumerable:!0})})(s,{DEFAULT_BACKLOG_STRINGS:()=>Ue,applyOps:()=>jt,archiveCandidates:()=>Mt,backlogFileNameOf:()=>D,blockedByThis:()=>b,blockersOf:()=>f,boardFileUrl:()=>Wt,createDefaultArchiveContent:()=>ae,createDefaultBoardContent:()=>ne,deepEqual:()=>u,diffStats:()=>h,diffToOps:()=>_t,formatHousekeepingSpec:()=>T,generateArchive:()=>te,generateBoard:()=>ee,generateTaskBlock:()=>Q,getHousekeeping:()=>Ot,hasCycle:()=>v,hasSourceComment:()=>J,indexTasks:()=>g,isFinished:()=>k,looksLikeBoard:()=>Gt,normalizeConfig:()=>Y,orderedTasks:()=>se,parseArchive:()=>q,parseBacklog:()=>U,parseBoard:()=>P,parseHousekeepingSpec:()=>B,parseTaskIds:()=>H,resolveBacklogFile:()=>L,resolveBoardUrl:()=>Vt,staleBacklogIds:()=>Pt,taskToMarkdown:()=>Ut,taskToPlainText:()=>qt});var r=class extends Error{constructor(e,t,n={}){super(t),this.name="BoardError",this.code=e,Object.assign(this,n)}toJSON(){const{code:e,message:t,hint:n,valid:a,details:s}=this;return{code:e,message:t,...n&&{hint:n},...a&&{valid:a},...s&&{details:s}}}};function o(e){return e.match(/[^\n]*\n|[^\n]+$/g)||[]}var i=e=>e.replace(/\r?\n$/,""),l=e=>(/\r?\n$/.exec(e)||[""])[0];var c=(e,t)=>e.replace(/\r?\n/g,t);var d=e=>function(e,t=0){let n=3735928559^t,a=1103547991^t;for(let t=0;t<e.length;t++){const s=e.charCodeAt(t);n=Math.imul(n^s,2654435761),a=Math.imul(a^s,1597334677)}return n=Math.imul(n^n>>>16,2246822507)^Math.imul(a^a>>>13,3266489909),a=Math.imul(a^a>>>16,2246822507)^Math.imul(n^n>>>13,3266489909),4294967296*(2097151&a)+(n>>>0)}(e).toString(36);function u(e,t){if(e===t)return!0;if("object"!=typeof e||"object"!=typeof t||null===e||null===t)return!1;if(Array.isArray(e)!==Array.isArray(t))return!1;const n=Object.keys(e),a=Object.keys(t);return n.length===a.length&&n.every(n=>Object.prototype.hasOwnProperty.call(t,n)&&u(e[n],t[n]))}function p(e,t,n=""){if(u(e,t))return null;if("object"!=typeof e||"object"!=typeof t||null===e||null===t||Array.isArray(e)!==Array.isArray(t))return{path:n,expected:e,actual:t};for(const a of new Set([...Object.keys(e),...Object.keys(t)])){const s=p(e[a],t[a],`${n}/${a}`);if(s)return s}return null}function h(e,t){const n=function(e,t){const n=o(e),a=o(t);let s=0;for(;s<n.length&&s<a.length&&n[s]===a[s];)s++;let r=0;for(;r<n.length-s&&r<a.length-s&&n[n.length-1-r]===a[a.length-1-r];)r++;const i=n.slice(s,n.length-r),l=a.slice(s,a.length-r);if(!i.length&&!l.length)return[];if(i.length*l.length>4e6)return[{at:s,del:i,add:l}];const c=i.length,d=l.length,u=Array.from({length:c+1},()=>new Uint32Array(d+1));for(let e=c-1;e>=0;e--)for(let t=d-1;t>=0;t--)u[e][t]=i[e]===l[t]?u[e+1][t+1]+1:Math.max(u[e+1][t],u[e][t+1]);const p=[];let h=0,m=0,g=null;const k=()=>{g&&(p.push(g),g=null)};for(;h<c||m<d;)h<c&&m<d&&i[h]===l[m]?(k(),h++,m++):(g||(g={at:s+h,del:[],add:[]}),m<d&&(h===c||u[h][m+1]>=u[h+1][m])?g.add.push(l[m++]):g.del.push(i[h++]));return k(),p}(e,t);return{hunks:n.length,removed:n.reduce((e,t)=>e+t.del.length,0),added:n.reduce((e,t)=>e+t.add.length,0),firstLine:n.length?n[0].at:null,lastLine:n.length?Math.max(...n.map(e=>e.at+e.del.length)):null}}var m="Blocked by(?=\\*\\*\\s*:[ \\t]*(?:[Tt][Aa][Ss][Kk]-\\d+[\\s,]*)+$)";function g({board:e=[],backlog:t=[],archive:n=[]}){const a=new Map;for(const[s,r]of[["archive",n],["backlog",t],["board",e]])for(const e of r)a.set(e.id,{task:e,where:s});return a}function k(e,t){return!!e&&("archive"===e.where||t.length>0&&e.task.status===t[t.length-1].id)}function f(e,t,n){const a={open:[],done:[],unknown:[]};for(const s of e.blockedBy||[]){const e=t.get(s);e?(k(e,n)?a.done:a.open).push(e):a.unknown.push(s)}return a}function b(e,t){return[...t.values()].filter(({task:t})=>(t.blockedBy||[]).includes(e))}function v(e,t){const n=new Set,a=s=>{for(const r of t.get(s)?.task.blockedBy||[]){if(r===e)return!0;if(!n.has(r)&&(n.add(r),a(r)))return!0}return!1};return a(e)}var y={doneAfterDays:30,backlogAfterDays:90,max:200,backlogFileAbove:100},w=/^(done|completed?|closed|finished|released|shipped)$/i,S=/backlog/i,x=e=>e.replace(/[^\w\s-]/g,"").trim();function B(e){if(null==e)return null;const t={enabled:!0};for(const n of String(e).split("|").map(e=>e.trim()).filter(Boolean)){if(/^off$/i.test(n))return{enabled:!1};const[e,...a]=n.split(/\s+/),[s,r]=e.split("="),o=Object.fromEntries(a.map(e=>e.split("=")).filter(e=>2===e.length).map(([e,t])=>[e.toLowerCase(),t])),i=void 0!==o.after&&/^\d+d?$/i.test(o.after)?parseInt(o.after,10):void 0,l=s.toLowerCase();"done"===l||"backlog"===l?t[l]={...r&&{column:r},...void 0!==i&&{afterDays:i}}:"max"===l&&/^\d+$/.test(r||"")&&(t.max=parseInt(r,10))}return t}function T(e){if(!e)return"";if(!1===e.enabled)return"off";const t=[],n=(e,n)=>{n&&t.push([`${e}=${n.column||""}`.replace(/=$/,""),void 0!==n.afterDays?`after=${n.afterDays}d`:""].filter(Boolean).join(" "))};return n("done",e.done),n("backlog",e.backlog),void 0!==e.max&&t.push(`max=${e.max}`),t.join(" | ")||"on"}function $(e){const t=e.housekeeping||{enabled:!0},n=e.columns||[],a=e=>(n.find(t=>e.test(t.id)||e.test(x(t.name)))||{}).id||null,s=e=>e&&n.some(t=>t.id===e)?e:null;return{enabled:!1!==t.enabled,done:{column:s(t.done&&t.done.column)||(t.done&&t.done.column?null:a(w)),afterDays:(t.done&&t.done.afterDays)??y.doneAfterDays},backlog:{column:s(t.backlog&&t.backlog.column)||(t.backlog&&t.backlog.column?null:a(S)),afterDays:(t.backlog&&t.backlog.afterDays)??y.backlogAfterDays},max:t.max??y.max,backlogFileAbove:y.backlogFileAbove}}var E="backlog.md",C=new Set(["kanban.md","archive.md"]),A=e=>/^[\w.-]+\.md$/.test(e)&&!e.includes("..")&&!C.has(e.toLowerCase());function I(e){if(null==e)return null;const t=String(e).trim();if(!t||/^off$/i.test(t))return null;const n=/^(\S+?)\s*(?:\(([^)]+)\))?\s*$/.exec(t);return n&&A(n[1])?{file:n[1],...n[2]&&{column:n[2].trim()}}:null}function F(e){return e?e.column?`${e.file} (${e.column})`:e.file:"off"}function L(e){const t=e.backlogFile;if(!t)return null;const n=e.columns||[],a=(t.column&&n.some(e=>e.id===t.column)?t.column:null)||(t.column?null:$({...e,housekeeping:null}).backlog.column);return a?{file:t.file,column:a}:null}function D(e){const t=/\*\*Backlog file\*\*:\s*(.+)/.exec(e),n=t&&I(t[1].trim());return n?n.file:E}function O(e){return e.toLowerCase().replace(/[^\w\s-]/g,"").trim().replace(/\s+/g,"-")}function P(e){const t=[],n={lastTaskId:0,columns:[],categories:[],users:[],priorities:[],tags:[],housekeeping:null,backlogFile:null,boardTitle:"Kanban Board"},a=e.split("\n",1)[0].replace(/\r$/,"").match(/^#[ \t]+(.+?)[ \t]*$/);a&&(n.boardTitle=a[1].trim());const s=e.match(/<!-- Config: Last Task ID: (\d+) -->/);s&&(n.lastTaskId=parseInt(s[1]));const r=e.match(/## ⚙️ Configuration\s+([\s\S]*?)(?:\n---|\n##\s|$)/);if(r){const e=r[1],t=e.match(/\*\*Columns\*\*:\s*(.+)/);if(t){const e=new Set;n.columns=t[1].split("|").map((t,n)=>{if(!(t=t.trim()))return null;const a=t.match(/^(.+?)\s*\((.+?)\)\s*$/);let s,r;a?(s=a[1].trim(),r=a[2].trim()):(s=t,r=O(t)||"col"+(n+1));let o=r,i=2;for(;e.has(o);)o=`${r}-${i++}`;return e.add(o),{name:s,id:o}}).filter(Boolean)}const a=e.match(/\*\*Categories\*\*:\s*(.+)/);a&&(n.categories=a[1].split(",").map(e=>e.trim()).filter(Boolean));const s=e.match(/\*\*Users\*\*:\s*(.+)/);s&&(n.users=s[1].split(",").map(e=>e.trim()).filter(Boolean));const o=e.match(/\*\*Priorities\*\*:\s*(.+)/);o&&(n.priorities=o[1].split("|").map(e=>e.trim()).filter(Boolean));const i=e.match(/\*\*Backlog file\*\*:\s*(.+)/);i&&(n.backlogFile=I(i[1].trim()));const l=e.match(/\*\*Housekeeping\*\*:\s*(.+)/);l&&(n.housekeeping=B(l[1].trim()));const c=e.match(/\*\*Tags\*\*:\s*(.+)/);c&&(n.tags=(c[1].match(/#[\w-]+/g)||[]).map(e=>e.slice(1)))}0===n.columns.length&&(n.columns=[{name:"📝 To Do",id:"todo"},{name:"🚀 In Progress",id:"in-progress"},{name:"👀 In Review",id:"in-review"},{name:"✅ Done",id:"done"}]),0===n.categories.length&&(n.categories=["Frontend","Backend","Design","DevOps","Tests","Documentation"]),0===n.users.length&&(n.users=["@user (User)"]),0===n.priorities.length&&(n.priorities=["🔴 Critical","🟠 High","🟡 Medium","🟢 Low"]),0===n.tags.length&&(n.tags=["bug","feature","ui","backend","urgent","refactor","docs","test"]);const o=/<!--\s*Format:\s*v(\d+)\s*-->/i.exec(e),i=!!o,l=/(^|\n)##\s+Tasks\s*(\n|$)/.test(e),c=[...e.matchAll(/(?:^|\n)##\s+([^\r\n]+?)\s*(?:\n|$)/g)].map(e=>e[1].trim()),d=n.columns.some(t=>{return new RegExp(`(^|\\n)##\\s+${n=t.name,n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\s*(\\n|$)`).test(e)||!l&&c.some(e=>O(e)===t.id);var n}),u=i||l&&!d;if(u){const a=e.replace(/^[\s\S]*?(?:^|\n)##\s+Tasks\s*(?:\n|$)/,"");t.push(...M(a,n.columns[0].id,!0,n.columns))}else n.columns.forEach(a=>{t.push(...function(e,t,n,a=!1,s=[]){const r=e.split(/\n##\s+/);for(let e of r)if(e.startsWith(t))return M(e.substring(t.length).trim(),n,a,s);for(let e of r){const t=e.search(/\r?\n/);if(O((t>=0?e.slice(0,t):e).trim())===n)return M(t>=0?e.slice(t).trim():"",n,a,s)}return[]}(e,a.name,a.id,!1,n.columns))});return{config:n,tasks:t,formatV2:u,formatVersion:o?parseInt(o[1],10):u?2:1}}function M(e,t,n=!1,a=[]){const s=[],r=[...e.matchAll(/^###\s+TASK-(\d+)\s*\|\s*(.+?)\s*$/gm)];return r.forEach((o,i)=>{const l=o[2].trim();if(!l)return;const c=o.index+o[0].length,d=i+1<r.length?r[i+1].index:e.length,u=e.slice(c,d),p=function(e,t,n,a,s=!1,r=[]){const o=n.split("\n"),i=function(e){const t={};for(const n of e){if(K.test(n))break;if(_.test(n))for(const e of n.split(j)){const n=z.exec(e);n&&!(n[1]in t)&&(t[n[1]]=n[2])}}return t}(o);let l=a;if(s&&void 0!==i.Status){const e=i.Status.trim();r.some(t=>t.id===e)&&(l=e)}const c={id:e,title:t.trim(),status:l,order:null,priority:"",category:"",assignees:[],tags:[],blockedBy:[],created:"",started:"",due:"",completed:"",description:"",subtasks:[],notes:"",extra:""};void 0!==i.Order&&/^\d+$/.test(i.Order)&&(c.order=parseInt(i.Order,10));void 0!==i.Priority&&(c.priority=i.Priority);void 0!==i.Category&&(c.category=i.Category);void 0!==i.Assigned&&(c.assignees=i.Assigned.split(",").map(e=>e.trim()).filter(Boolean));for(const[e,t]of[["Created","created"],["Started","started"],["Due","due"],["Finished","completed"]]){const n=void 0!==i[e]&&i[e].match(/^[\d-]+/);n&&(c[t]=n[0])}void 0!==i.Tags&&(c.tags=i.Tags.match(/#[\w-]+/g)||[]);void 0!==i["Blocked by"]&&(c.blockedBy=H(i["Blocked by"]));let d=[],u=!1;for(let e of o)if(!_.test(e)&&!e.match(/^\*\*(Status|Order|Priority|Category|Assigned|Created|Started|Due|Finished|Tags)\*\*/)){if(e.match(/^\*\*(Subtasks|Notes|Links|Review|Dependencies)\*\*/))break;e.trim()&&!u&&(u=!0),u&&e.trim()&&d.push(e.trim())}c.description=d.join("\n");let p=!1;for(let e of o)if(/^\s*\*\*Subtasks\*\*/i.test(e))p=!0;else if(p&&/^\s*\*\*[^*\r\n]+\*\*\s*:/.test(e)&&(p=!1),p){const t=e.match(/- \[(x| )\] (.+)/);t&&c.subtasks.push({completed:"x"===t[1],text:t[2].trim()})}const h=n.match(/\*\*Notes\*\*:\s*\n([\s\S]*?)$/);h&&(c.notes=h[1].trim());const m=/^\s*\*\*(Links|Review|Dependencies)\*\*\s*:/i,g=/^\s*\*\*(Status|Order|Priority|Category|Assigned|Created|Started|Due|Finished|Tags|Subtasks)\*\*/i,k=[];let f=!1;for(let e of o){if(/^\s*\*\*Notes\*\*/i.test(e))break;m.test(e)?(f=!0,k.push(e)):(f&&g.test(e)&&(f=!1),f&&k.push(e))}return c.extra=k.join("\n").trim(),c}("TASK-"+o[1].padStart(3,"0"),l,u,t,n,a);p&&s.push(p)}),s}function H(e){const t=(String(e).match(/\bTASK-\d+\b/gi)||[]).map(e=>"TASK-"+String(parseInt(e.slice(5),10)).padStart(3,"0"));return[...new Set(t)]}var R=" ",N=`Status|Order|Priority|Category|Assigned|Created|Started|Due|Finished|Tags|${m}`,_=new RegExp(`^\\s*\\*\\*(?:${N})\\*\\*\\s*:`),j=new RegExp(`\\s*\\|\\s*(?=\\*\\*(?:${N})\\*\\*\\s*:)`),z=new RegExp(`^\\s*\\*\\*(${N})\\*\\*\\s*:\\s*(.*?)\\s*$`),K=/^\*\*(Subtasks|Notes|Links|Review|Dependencies)\*\*/;function q(e,t=[]){const n=/<!--\s*Format:\s*v(\d+)\s*-->/i.exec(e);return{tasks:M(e,"archived",!0,t),formatV2:!!n&&parseInt(n[1],10)>=2,formatVersion:n?parseInt(n[1],10):1}}function U(e,t=[],n="backlog"){const a=/<!--\s*Format:\s*v(\d+)\s*-->/i.exec(e);return{tasks:M(e,n,!0,t),formatVersion:a?parseInt(a[1],10):1}}var V=["Frontend","Backend","Design","DevOps","Tests","Documentation"],W=["@user (User)"],G=["🔴 Critical","🟠 High","🟡 Medium","🟢 Low"],Z=["bug","feature","ui","backend","urgent","refactor","docs","test"];function Y(e){const t={...e};return t.categories=[...new Set(e.categories||[])],t.users=[...new Set(e.users||[])],t.tags=[...new Set(e.tags||[])],0===t.categories.length&&(t.categories=[...V]),0===t.users.length&&(t.users=[...W]),t.priorities&&0!==t.priorities.length||(t.priorities=[...G]),0===t.tags.length&&(t.tags=[...Z]),t}function Q(e){let t=`### ${e.id} | ${e.title}\n`;const n=(e,t)=>`**${e}**: ${t}${R}\n`;return t+=n("Status",e.status),null!=e.order&&(t+=n("Order",e.order)),e.priority&&(t+=n("Priority",e.priority)),e.category&&(t+=n("Category",e.category)),e.assignees.length>0&&(t+=n("Assigned",e.assignees.join(", "))),e.created&&(t+=n("Created",e.created)),e.started&&(t+=n("Started",e.started)),e.due&&(t+=n("Due",e.due)),e.completed&&(t+=n("Finished",e.completed)),e.tags.length>0&&(t+=n("Tags",e.tags.join(" "))),e.blockedBy&&e.blockedBy.length>0&&(t+=n("Blocked by",e.blockedBy.join(", "))),e.description&&(t+=`\n${e.description}\n`),e.subtasks.length>0&&(t+="\n**Subtasks**:\n",e.subtasks.forEach(e=>{t+=`- [${e.completed?"x":" "}] ${e.text}\n`})),e.extra&&(t+=`\n${e.extra}\n`),e.notes&&(t+=`\n**Notes**:\n${e.notes}\n`),t+"\n"}var X="\x3c!-- Kanban.md board. Format, rules and tools (app, CLI, MCP): https://github.com/ashikahmad/Kanban.md --\x3e";function J(e){if(!e)return!1;const t=e.search(/^###\s+TASK-/m);return/<!--\s*Kanban\.md board\b/i.test(t<0?e:e.slice(0,t))}function ee({config:e,tasks:t},{sourceComment:n=!1}={}){const a=Y(e);let s=`# ${a.boardTitle&&a.boardTitle.trim()||"Kanban Board"}\n\n\x3c!-- Config: Last Task ID: ${a.lastTaskId} --\x3e\n\x3c!-- Format: v3 --\x3e\n${n?X+"\n":""}\n`;return s+="## ⚙️ Configuration\n\n",s+=`**Columns**: ${a.columns.map(e=>`${e.name} (${e.id})`).join(" | ")}\n\n`,s+=`**Categories**: ${a.categories.join(", ")}\n\n`,s+=`**Users**: ${a.users.join(", ")}\n\n`,s+=`**Priorities**: ${a.priorities.join(" | ")}\n\n`,s+=`**Tags**: ${a.tags.map(e=>"#"+e).join(" ")}\n\n`,a.housekeeping&&(s+=`**Housekeeping**: ${T(a.housekeeping)}\n\n`),a.backlogFile&&(s+=`**Backlog file**: ${F(a.backlogFile)}\n\n`),s+="---\n\n",s+="## Tasks\n\n",t.forEach(e=>{s+=Q(e)}),s}function te(e,t,{sourceComment:n=!1}={}){let a=`${t.title}\n\x3c!-- Format: v3 --\x3e\n${n?X+"\n":""}\n${t.desc}\n\n${t.section}\n\n`;return e.forEach(e=>{a+=Q(e)}),a}function ne(){return`# Kanban Board\n\n\x3c!-- Config: Last Task ID: 0 --\x3e\n\x3c!-- Format: v3 --\x3e\n${X}\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 ae(e){return`${e.title}\n${X}\n\n${e.desc}\n\n${e.section}\n\n`}function se(e){return e.map((e,t)=>({task:e,i:t})).sort((e,t)=>(null==e.task.order)-(null==t.task.order)||(e.task.order??0)-(t.task.order??0)||e.i-t.i).map(e=>e.task)}var re=3,oe=class{constructor(e){this.lines=o(e),this.eol=function(e){const t=e.indexOf("\n");return t>0&&"\r"===e[t-1]?"\r\n":"\n"}(e)}toString(){return this.lines.join("")}},ie=class extends Error{},le=/^###\s+TASK-(\d+)\s*\|\s*(.+?)\s*$/,ce=new RegExp(`^\\*\\*(?:Status|Order|Priority|Category|Assigned|Created|Started|Due|Finished|Tags|${m})\\*\\*`),de=/^\*\*(Subtasks|Notes|Links|Review|Dependencies)\*\*/,ue=[["status","Status"],["order","Order"],["priority","Priority"],["category","Category"],["assignees","Assigned"],["created","Created"],["started","Started"],["due","Due"],["completed","Finished"],["tags","Tags"],["blockedBy","Blocked by"]],pe=ue.map(([,e])=>"Blocked by"===e?m:e).join("|"),he=new RegExp(`^(\\s*\\*\\*(${pe})\\*\\*\\s*:[ \\t]*)(.*)$`),me=e=>"TASK-"+e.padStart(3,"0"),ge=e=>""===i(e).trim();function ke(e){const t=e.findIndex(e=>/^##\s+Tasks\s*$/.test(i(e)));return t<0?-1:t+1}function fe(e,t=0){const n=[];for(let a=Math.max(0,t);a<e.length;a++){const t=le.exec(i(e[a]));t&&n.push({id:me(t[1]),start:a})}return n.map((t,a)=>({...t,end:a+1<n.length?n[a+1].start:e.length}))}function be(e,t){return fe(e.lines,"board"===t?Math.max(0,ke(e.lines)):0)}function ve(e,t,n){const a=be(e,t).filter(e=>e.id===n);return 1!==a.length?null:{...a[0],kind:t}}var ye=(e,t)=>e.lines.slice(t.start,t.end).join("");function we(e,t){const{start:n,end:a}=t,s=t=>i(e.lines[t]),r={header:n,fields:{},desc:null,descInterleaved:!1,subHeaders:[],subItems:[],notes:-1,extraStart:-1,stop:a,lastContent:n};let o=!1;for(let t=n+1;t<a;t++){const n=s(t);if(n.trim()&&(r.lastContent=t),r.stop===a&&de.test(n)&&(r.stop=t),r.notes<0&&/\*\*Notes\*\*:[ \t\r]*$/.test(n)&&l(e.lines[t])&&(r.notes=t),r.extraStart<0&&/^\s*\*\*(Links|Review|Dependencies)\*\*\s*:/i.test(n)&&(r.extraStart=t),/^\s*\*\*Subtasks\*\*/i.test(n))o=!0,r.subHeaders.push(t);else if(o&&/^\s*\*\*[^*\r\n]+\*\*\s*:/.test(n)&&(o=!1),o){const e=/- \[(x| )\] (.+)/.exec(n);e&&r.subItems.push({line:t,box:e.index+3,completed:"x"===e[1],text:e[2].trim()})}}for(let e=n+1;e<r.stop;e++){const t=s(e),n=he.exec(t);if(!n)continue;const a=t.split(new RegExp(`\\s*\\|\\s*(?=\\*\\*(?:${pe})\\*\\*\\s*:)`));a.forEach((t,s)=>{const o=new RegExp(`^\\s*\\*\\*(${pe})\\*\\*\\s*:\\s*(.*?)\\s*$`).exec(t);o&&!r.fields[o[1]]&&(r.fields[o[1]]={line:e,valueStart:0===s?n[1].length:-1,value:o[2],joined:a.length>1})})}const c=[];let d=!1;for(let e=n+1;e<r.stop;e++){const t=s(e);ce.test(t)||he.test(t)?c.length&&(d=!0):t.trim()&&c.push(e)}return c.length&&(r.desc={first:c[0],last:c[c.length-1],count:c.length}),r.descInterleaved=d,r}var Se=(e,t)=>t+e.eol;function xe(e,t){t>=0&&t<e.lines.length&&!l(e.lines[t])&&(e.lines[t]+=e.eol)}function Be(e,t,n){t>0&&xe(e,t-1),e.lines.splice(t,0,...n.map(t=>Se(e,t)))}function Te(e,t,n,a,s){const r=l(e.lines[t]),o=i(e.lines[t]);e.lines[t]=o.slice(0,n)+s+o.slice(a)+r}function $e(e,t,n){e.lines.splice(t,n-t)}function Ee(e,t,n){t>0&&ge(e.lines[t-1])&&t--,$e(e,t,n)}function Ce(e,t,n,a){const s=ue.findIndex(([e])=>e===n),r=ue[s][1],o=function(e,t){return"assignees"===e?t.length?t.join(", "):null:"tags"===e?t.length?t.join(" "):null:"blockedBy"===e?t&&t.length?t.join(", "):null:"order"===e?null==t?null:String(t):t?String(t):null}(n,a),l=we(e,t),c=l.fields[r];if(c){if(c.joined)throw new ie("metadata fields joined on one line");return void(null===o?$e(e,c.line,c.line+1):Te(e,c.line,c.valueStart,i(e.lines[c.line]).trimEnd().length,o))}if(null===o)return;let d=t.start+1;for(let e=0;e<s;e++){const t=l.fields[ue[e][1]];t&&(d=Math.max(d,t.line+1))}Be(e,d,[`**${r}**: ${o}${R}`])}var Ae=e=>`- [${e.completed?"x":" "}] ${e.text}`;function Ie(e,t,n,a){const s=function(e,t,n){const a=[n.extraStart,n.notes].filter(e=>e>=0);return a.length?Math.min(...a):n.lastContent+1}(0,0,n),r=["","**Subtasks**:",...a.map(Ae)];s>0&&ge(e.lines[s-1])&&r.shift(),s<t.end&&!ge(e.lines[s])&&r.push(""),Be(e,s,r)}function Fe(e,t,n){const a=we(e,t),s=a.subItems;if(s.length===n.length&&s.every((e,t)=>e.text===n[t].text))return void n.forEach((n,a)=>{n.completed!==s[a].completed&&function(e,t,n,a){const s=we(e,t).subItems[n];if(!s)throw new ie("no such subtask");Te(e,s.line,s.box,s.box+1,a?"x":" ")}(e,t,a,n.completed)});if(a.subHeaders.length>1)throw new ie("several Subtasks sections");if(!s.length){if(a.subHeaders.length)throw new ie("empty Subtasks section");return void(n.length&&Ie(e,t,a,n))}const r=s[0].line,o=s[s.length-1].line;for(let t=r;t<=o;t++)if(!s.some(e=>e.line===t)&&!ge(e.lines[t]))throw new ie("foreign lines among subtasks");n.length?(e.lines.splice(r,o-r+1,...n.map(t=>Se(e,Ae(t)))),xe(e,r+n.length-1)):Ee(e,a.subHeaders.length?a.subHeaders[0]:r,o+1)}function Le(e,t,n){const a=we(e,t),s=n?n.split(/\r?\n/):[];a.notes>=0?s.length?(e.lines.splice(a.notes+1,a.lastContent-a.notes,...s.map(t=>Se(e,t))),xe(e,a.notes+s.length)):Ee(e,a.notes,a.lastContent+1):s.length&&Be(e,a.lastContent+1,["","**Notes**:",...s])}function De(e,t,n){const a=o(c(Q(n),e.eol));e.lines.splice(t.start,t.end-t.start,...a)}function Oe(e,t){const n=[...t];return n.length?(l(n[n.length-1])||(n[n.length-1]+=e.eol),ge(n[n.length-1])||n.push(e.eol),n):n}function Pe(e,t,n){const a=be(e,t),s=a.map(t=>e.lines.slice(t.start,t.end)),r=e.lines.slice(0,a[0].start),o=n.flatMap((t,a)=>a===n.length-1?s[t]:Oe(e,s[t]));e.lines=[...r,...o]}var Me=(e,t)=>e.map(e=>c(e,t));function He(e,t){return e.lines.splice(t.start,t.end-t.start)}function Re(e,t,n){const a=Oe(e,n);t>0&&xe(e,t-1),t>0&&!ge(e.lines[t-1])&&a.unshift(e.eol),e.lines.splice(t,0,...a)}var Ne={columns:"Columns",categories:"Categories",users:"Users",priorities:"Priorities",tags:"Tags",housekeeping:"Housekeeping",backlogFile:"Backlog file"};function _e(e){const t=e.lines.findIndex(e=>i(e).startsWith("## ⚙️ Configuration"));if(t<0)return null;let n=e.lines.length;for(let a=t+1;a<e.lines.length;a++)if(/^---/.test(e.lines[a])||/^##\s/.test(e.lines[a])){n=a;break}return{from:t+1,to:n}}function je(e,t,n,a){const s=Ne[t],r=_e(e);if(!r){const t=o(ee({config:a,tasks:[]})),n=t.findIndex(e=>i(e).startsWith("## ⚙️ Configuration")),s=t.findIndex(e=>/^##\s+Tasks\s*$/.test(i(e)));return void Be(e,Math.max(0,ke(e.lines)-1),t.slice(n,s).map(i))}const l=new RegExp(`\\*\\*${s}\\*\\*:\\s*(.+)`);for(let t=r.from;t<r.to;t++){const a=l.exec(i(e.lines[t]));if(a)return void Te(e,t,a.index,i(e.lines[t]).length,`**${s}**: ${n}`)}const c=r.to>0&&!ge(e.lines[r.to-1]);Be(e,r.to,[...c?[""]:[],`**${s}**: ${n}`,""])}function ze(e,t){const n=_e(e);if(!n)return;const a=new RegExp(`\\*\\*${Ne[t]}\\*\\*:\\s*(.+)`);for(let t=n.from;t<n.to;t++)if(a.test(i(e.lines[t])))return void $e(e,t,t+(t+1<e.lines.length&&ge(e.lines[t+1])?2:1))}var Ke={columns:e=>e.map(e=>`${e.name} (${e.id})`).join(" | "),categories:e=>e.join(", "),users:e=>e.join(", "),priorities:e=>e.join(" | "),tags:e=>e.map(e=>"#"+e).join(" "),housekeeping:e=>T(e),backlogFile:e=>F(e)},qe={title:"# Task Archive",desc:"> Archived tasks",section:"## ✅ Archives"},Ue={title:"# Backlog",desc:"> Parked tasks, kept out of the board file. A task is here exactly while its status is the backlog column.",section:"## 📥 Backlog"},Ve=["title","status","order","priority","category","assignees","tags","blockedBy","created","started","due","completed","description","subtasks","notes"],We=["createTask","updateTask","moveTask","sortTasks","archiveTasks","setHousekeeping","setBacklogFile","toggleSubtask","addSubtask","appendNote","archiveTask","restoreTask","deleteTask","setConfigList","setColumns"],Ge=["categories","users","priorities","tags"],Ze=e=>JSON.parse(JSON.stringify(e)),Ye=()=>(new Date).toISOString().slice(0,10),Qe=e=>e.replace(/[^\w\s]+/,"").trim();function Xe(e,t,n){throw new r(e,t,n)}var Je=e=>"string"==typeof e,et=(e,t)=>{Je(e)||Xe("invalid_field",`${t} must be a string`);const n=e.trim();return/[\r\n]/.test(n)&&Xe("invalid_field",`${t} must be a single line`),n},tt=(e,t)=>(Array.isArray(e)?e:Je(e)?e.split(","):Xe("invalid_field",`${t} must be an array or a comma separated string`)).map(e=>et(e,t)).filter(Boolean);function nt(e,t,{partial:n}){const a={};for(const t of Object.keys(e))Ve.includes(t)||Xe("unknown_field",`Unknown task field "${t}"`,{valid:Ve});const s=t=>void 0!==e[t];if(s("title")?(a.title=et(e.title,"title"),a.title||Xe("invalid_field","title must not be empty")):n||Xe("missing_field","title is required"),s("status")){a.status=et(e.status,"status");const n=t.columns.map(e=>e.id);n.includes(a.status)||Xe("unknown_status",`Unknown status "${a.status}"`,{valid:n,hint:"Use a column id from the board configuration."})}if(s("order")){const t=e.order;if(""===t||null===t)a.order=null;else{const e="string"==typeof t&&/^\d+$/.test(t.trim())?parseInt(t,10):t;(!Number.isInteger(e)||e<0)&&Xe("invalid_field","order must be a non-negative integer, or empty to clear it"),a.order=e}}if(s("priority")&&(a.priority=et(e.priority,"priority"),a.priority)){const e=(t.priorities||[]).map(Qe);e.length&&!e.includes(a.priority)&&Xe("unknown_priority",`Unknown priority "${a.priority}"`,{valid:e})}if(s("category")&&(a.category=et(e.category,"category"),/\|\s*\*\*\w+\*\*\s*:/.test(a.category)&&Xe("invalid_field","category must not look like another metadata field")),s("assignees")&&(a.assignees=tt(e.assignees,"assignees"),a.assignees.some(e=>e.includes(","))&&Xe("invalid_field",'assignee names must not contain ","')),s("tags")){const t=tt(Array.isArray(e.tags)?e.tags:Je(e.tags)?e.tags.split(/[\s,]+/):e.tags,"tags").map(e=>e.startsWith("#")?e:"#"+e);for(const e of t)/^#[\w-]+$/.test(e)||Xe("invalid_tag",`Invalid tag "${e}"`,{hint:'Tags are letters, digits, "_" and "-".'});a.tags=[...new Set(t)]}if(s("blockedBy")){const t=(Array.isArray(e.blockedBy)?e.blockedBy:Je(e.blockedBy)?e.blockedBy.split(/[\s,]+/):Xe("invalid_field","blockedBy must be an array or a comma separated string")).map(e=>et(e,"blockedBy")).filter(Boolean);for(const e of t)/^(?:TASK-)?\d+$/i.test(e)||Xe("invalid_field",`Invalid task id "${e}" in blockedBy`,{hint:"Ids look like TASK-014."});a.blockedBy=H(t.map(e=>/^\d+$/.test(e)?"TASK-"+e:e).join(" "))}for(const t of["created","started","due","completed"]){if(!s(t))continue;const n=et(e[t],t);!n||/^\d{4}-\d{2}-\d{2}$/.test(n)&&!Number.isNaN(Date.parse(n))||Xe("invalid_date",`${t} must be YYYY-MM-DD`,{hint:`Got "${n}".`}),a[t]=n}if(s("description")){Je(e.description)||Xe("invalid_field","description must be a string");const t=e.description.split(/\r?\n/).map(e=>e.trim()).filter(Boolean);for(const e of t)/^#{1,6}\s/.test(e)&&Xe("invalid_description","description must not contain markdown headings",{hint:`Offending line: "${e}"`}),(/^\*\*(Status|Priority|Category|Assigned|Created|Started|Due|Finished|Tags|Blocked by|Subtasks|Notes|Links|Review|Dependencies)\*\*/.test(e)||/\*\*Notes\*\*:\s*$/.test(e))&&Xe("invalid_description","description must not start a line with a field or section label",{hint:`Offending line: "${e}"`});a.description=t.join("\n")}return s("subtasks")&&(Array.isArray(e.subtasks)||Xe("invalid_field","subtasks must be an array of { text, completed }"),a.subtasks=e.subtasks.map(e=>({completed:!!e.completed,text:et(Je(e)?e:e.text,"subtask text")})),a.subtasks.some(e=>!e.text)&&Xe("invalid_field","subtask text must not be empty")),s("notes")&&(Je(e.notes)||Xe("invalid_field","notes must be a string"),a.notes=function(e){const t=e.replace(/\s+$/,"").replace(/^(\s*\r?\n)+/,"");t.split(/\r?\n/).some(e=>/^###\s+TASK-\d+\s*\|/.test(e))&&Xe("invalid_notes","notes must not contain a task header line");return t.replace(/\r\n/g,"\n")}(e.notes)),a}var at=["board","archive","backlog"],st=(e,t)=>"board"===t?e.state.tasks:e.state[t],rt=(e,t,n)=>{"board"===t?e.state.tasks=n:e.state[t]=n},ot=(e,t)=>"board"===t?"kanban.md":"archive"===t?"archive.md":e.backlogName;function it(e){if(!e.backlog){if(null!==e.strayBacklog&&!e.state.config.backlogFile){const t=U(e.strayBacklog,e.state.config.columns,"backlog");t.tasks.length&&Xe("backlog_file_exists",`${e.backlogName} already exists and holds ${t.tasks.length} task(s)`,{hint:"Move or rename that file first; it is not touched."})}e.backlog=new oe(c(te([],Ue,{sourceComment:!0}),e.board.eol))}return e.backlog}var lt=(e,t)=>"archive"===t?pt(e):"backlog"===t?it(e):e.board;function ct(e,t,n,a){const s=e[n],r=lt(e,a),o=ve(s,n,t.id);o||Xe("ambiguous_id",`${t.id} appears more than once in ${ot(e,n)}`,{hint:"Fix the duplicate id by hand first."});const i=Tt(He(s,o),t,s,r);Re(r,r.lines.length,i),rt(e,n,st(e,n).filter(e=>e!==t)),st(e,a).push(t),e.moves.push({from:n,to:a})}function dt(e){const t=L(e.state.config);if(t){for(const n of[...e.state.tasks])n.status===t.column&&(Et(e,{task:n,kind:"board",doc:e.board},n.status),ct(e,n,"board","backlog"));for(const n of[...e.state.backlog])n.status!==t.column&&(Et(e,{task:n,kind:"backlog",doc:e.backlog},n.status),ct(e,n,"backlog","board"))}}function ut(e,t){const n=e=>t.filter(t=>t.to===e).length-t.filter(t=>t.from===e).length,a=["archive","backlog","board"];return[...e].sort((e,t)=>n(t)-n(e)||a.indexOf(e)-a.indexOf(t))}function pt(e){return e.archive?(e.archiveVersion<re&&(fe(e.archive.lines).length>0&&ht(e),function(e,t=3){const n=e.lines.findIndex(e=>/<!--\s*Format:/i.test(e));n>=0?Te(e,n,0,i(e.lines[n]).length,`\x3c!-- Format: v${t} --\x3e`):Be(e,Math.min(1,e.lines.length),[`\x3c!-- Format: v${t} --\x3e`])}(e.archive),e.archiveVersion=re),e.archive):(e.archive=new oe(c(te([],qe,{sourceComment:!0}),e.board.eol)),e.archiveVersion=re,e.archive)}function ht(e){Xe("needs_migration",`archive.md uses format v${e.archiveVersion}; edits need v${re}`,{hint:"Run `kanbanmd migrate`, or open the board in the app."})}function mt(e,t,n,a=[]){const s=new Set([...e.state.tasks,...e.state.backlog,...e.state.archive].map(e=>e.id));for(const e of t)e===n&&Xe("invalid_field",`${e} cannot be blocked by itself`),a.includes(e)||s.has(e)||Xe("unknown_task",`blockedBy names ${e}, which does not exist`,{hint:"Ids look like TASK-014."})}function gt(e,t){const n=/^TASK-\d+$/i.test(t||"")?"TASK-"+t.slice(5).padStart(3,"0"):t,a=e.state.tasks.find(e=>e.id===n);if(a)return{task:a,kind:"board",doc:e.board};const s=e.state.backlog.find(e=>e.id===n);if(s)return{task:s,kind:"backlog",doc:e.backlog};const r=e.state.archive.find(e=>e.id===n);if(r)return e.archiveVersion<re&&ht(e),{task:r,kind:"archive",doc:e.archive};Xe("unknown_task",`No task ${t}`,{hint:"Ids look like TASK-014."})}function kt(e,t){const n=ve(t.doc,t.kind,t.task.id);return n||Xe("ambiguous_id",`${t.task.id} appears more than once in ${ot(e,t.kind)}`,{hint:"Fix the duplicate id by hand first."}),n}function ft(e,t,n){if(!n.ifMatch)return;const a=d(ye(t.doc,kt(e,t)));a!==n.ifMatch&&Xe("conflict",`${t.task.id} changed since you read it`,{details:{current:a,task:t.task}})}var bt=(e,t)=>JSON.stringify(e)===JSON.stringify(t);function vt(e,t,n,a){const s=t.doc,r=(t.kind,n.id,()=>kt(e,t));try{n.title!==a.title&&function(e,t,n){const a=/^(###\s+TASK-\d+\s*\|\s*)(.+?)(\s*)$/.exec(i(e.lines[t.start]));Te(e,t.start,a[1].length,a[1].length+a[2].length,n)}(s,r(),a.title);for(const e of["status","order","priority","category","assignees","created","started","due","completed","tags","blockedBy"])bt(n[e],a[e])||Ce(s,r(),e,a[e]);n.description!==a.description&&function(e,t,n){const a=we(e,t);if(a.descInterleaved)throw new ie("metadata inside description");const s=n?n.split("\n"):[];if(a.desc)return void(s.length?(e.lines.splice(a.desc.first,a.desc.last-a.desc.first+1,...s.map(t=>Se(e,t))),xe(e,a.desc.first+s.length-1)):Ee(e,a.desc.first,a.desc.last+1));if(!s.length)return;let r=t.start+1;for(let n=t.start+1;n<a.stop;n++)ce.test(i(e.lines[n]))&&(r=n+1);const o=["",...s],l=e.lines[r];void 0!==l&&r<t.end&&!ge(l)&&o.push(""),Be(e,r,o)}(s,r(),a.description),bt(n.subtasks,a.subtasks)||Fe(s,r(),a.subtasks),n.notes!==a.notes&&Le(s,r(),a.notes)}catch(e){if(!(e instanceof ie))throw e;De(s,r(),a)}}var yt=1e3,wt=e=>e.some(e=>null!=e.order);function St(e,t,n){const a=ve(e.board,"board",t.id);a||Xe("ambiguous_id",`${t.id} appears more than once in kanban.md`,{hint:"Fix the duplicate id by hand first."}),t.order=n,Ce(e.board,a,"order",n)}function xt(e){const t=e.state.tasks;if(!wt(t))return;let n=Math.max(...t.filter(e=>null!=e.order).map(e=>e.order));for(const a of t)null==a.order&&St(e,a,n+=yt)}var Bt={createTask(e,t,n){const a=nt(t.task||Xe("missing_field",'createTask needs a "task" object'),e.state.config,{partial:!1});a.blockedBy&&mt(e,a.blockedBy,null);const s={id:"",title:a.title,status:a.status||e.state.config.columns[0].id,priority:a.priority||"",category:a.category||"",assignees:a.assignees||[],tags:a.tags||[],blockedBy:a.blockedBy||[],created:a.created||n.today(),started:a.started||"",due:a.due||"",completed:a.completed||"",description:a.description||"",subtasks:a.subtasks||[],notes:a.notes||"",extra:"",order:void 0===a.order?null:a.order};s.notes=s.notes.replace(/\n/g,e.board.eol);const r=Math.max(e.state.config.lastTaskId,function(e){return[...e.state.tasks,...e.state.backlog,...e.state.archive].reduce((e,t)=>Math.max(e,parseInt((t.id.match(/(\d+)$/)||[])[1],10)||0),0)}(e))+1;var i,l;return s.id="TASK-"+String(r).padStart(3,"0"),void 0===a.order&&wt(e.state.tasks)&&(xt(e),s.order=Math.max(...e.state.tasks.map(e=>e.order))+yt),e.state.config.lastTaskId=r,e.state.tasks.push(s),i=e.board,l=Q(s),Re(i,i.lines.length,o(c(l,i.eol))),function(e,t){const n=/<!-- Config: Last Task ID: (\d+) -->/,a=e.lines.findIndex(e=>n.test(e));if(a>=0){const s=n.exec(e.lines[a]),r=s[1],o=s.index+s[0].indexOf(r);return void Te(e,a,o,o+r.length,String(t).padStart(r.length,"0"))}const s=`\x3c!-- Config: Last Task ID: ${t} --\x3e`;e.lines.length>1&&ge(e.lines[1])?Be(e,2,[s]):Be(e,Math.min(1,e.lines.length),["",s])}(e.board,r),{id:s.id,kind:"board"}},updateTask(e,t){const n=gt(e,t.id);ft(e,n,t);const a=nt(t.set||Xe("missing_field",'updateTask needs a "set" object'),e.state.config,{partial:!0});a.blockedBy&&mt(e,a.blockedBy,n.task.id,n.task.blockedBy||[]);const s=Ze(n.task);void 0!==a.notes&&(a.notes=a.notes.replace(/\n/g,n.doc.eol));const r=Object.assign(n.task,a);return vt(e,n,s,r),{id:r.id,kind:n.kind}},moveTask(e,t){let n=gt(e,t.id);"archive"===n.kind&&Xe("task_archived",`${n.task.id} is archived`,{hint:"Restore it first."}),ft(e,n,t);const{config:a}=e.state,s=void 0===t.status?n.task.status:nt({status:t.status},a,{partial:!0}).status;t.before&&t.atEnd&&Xe("invalid_field","give either before or atEnd, not both");const r=L(a),o=()=>{const t=Ze(n.task);n.task.status=s,vt(e,n,t,n.task)};if("backlog"===n.kind){if(s===r.column)return{id:n.task.id,kind:"backlog"};if(o(),dt(e),n=gt(e,t.id),!t.before&&!t.atEnd)return{id:n.task.id,kind:"board"}}else if(r&&s===r.column&&n.task.status!==s)return o(),{id:n.task.id,kind:"backlog"};const{tasks:i}=e.state,l=n.task;if(t.before){const e=i.find(e=>e.id===t.before);e&&e.id!==l.id||Xe("unknown_task",`No task ${t.before} to place it before`),e.status!==s&&Xe("before_not_in_column",`${e.id} is not in column "${s}"`)}const c=l.status!==s;if(!(!t.before&&!t.atEnd)&&wt(i)){xt(e),c&&(Ce(e.board,kt(e,n),"status",s),l.status=s);const a=se(i).filter(e=>e.status===s),r=a.indexOf(l),o=a.filter(e=>e!==l);if(t.before?a[r+1]&&a[r+1].id===t.before:a[a.length-1]===l)return{id:l.id,kind:"board"};if(t.before){const n=o.findIndex(e=>e.id===t.before),a=o[n].order,s=n>0?o[n-1].order:null,r=null===s?Math.floor(a/2):Math.floor((s+a)/2);if(r<a&&(null===s||r>s))St(e,l,r);else{const n=se(i).filter(e=>e!==l);n.splice(n.findIndex(e=>e.id===t.before),0,l),n.forEach((t,n)=>{t.order!==(n+1)*yt&&St(e,t,(n+1)*yt)})}}else{const t=o.length?o:i.filter(e=>e!==l);St(e,l,Math.max(0,...t.map(e=>e.order))+yt)}return{id:l.id,kind:"board"}}const d=i.filter(e=>e!==l);let u=-1;if(t.before)u=d.findIndex(e=>e.id===t.before);else if(t.atEnd){u=d.length;for(let e=d.length-1;e>=0;e--)if(d[e].status===s){u=e+1;break}}const p=u<0?i:[...d.slice(0,u),l,...d.slice(u)],h=p.some((e,t)=>e!==i[t]);if(c&&Ce(e.board,kt(e,n),"status",s),h){const t=He(e.board,kt(e,n)),a=u<d.length?ve(e.board,"board",d[u].id):null;Re(e.board,a?a.start:e.board.lines.length,t)}return l.status=s,e.state.tasks=p,{id:l.id,kind:"board"}},sortTasks(e,t){const n=t.mode,a=["blocks","ids","positions","clear"];n||Xe("missing_field","sortTasks needs a mode",{valid:a,hint:"blocks: reorder file blocks by id. ids / positions: write Order keys from id order / current order. clear: remove all Order keys."}),a.includes(n)||Xe("invalid_field",`Unknown sort mode "${n}"`,{valid:a}),t.by&&"id"!==t.by&&Xe("invalid_field",`Cannot sort by "${t.by}"`,{valid:["id"]});const s="desc"===t.order?-1:1,r=e=>parseInt((e.id.match(/(\d+)$/)||[])[1],10)||0,o=e.state.tasks;if("clear"===n){for(const t of o)null!=t.order&&St(e,t,null);return{mode:n}}if("ids"===n||"positions"===n){return("positions"===n?se(o):[...o].sort((e,t)=>s*(r(e)-r(t))||o.indexOf(e)-o.indexOf(t))).forEach((t,n)=>{t.order!==(n+1)*yt&&St(e,t,(n+1)*yt)}),{mode:n}}const i=t.scope||"board";["board","archive","both"].includes(i)||Xe("invalid_field",`Unknown scope "${i}"`,{valid:["board","archive","both"]});for(const t of["board","archive"]){if("both"!==i&&i!==t)continue;const n="board"===t?e.board:e.archive,a="board"===t?e.state.tasks:e.state.archive;if(!n||a.length<2)continue;const o=a.map((e,t)=>t).sort((e,t)=>s*(r(a[e])-r(a[t]))||e-t);if(o.every((e,t)=>e===t))continue;be(n,t).length!==a.length&&Xe("ambiguous_id","Could not match every task block in the file",{hint:"Fix duplicate or malformed task headers first."}),Pe(n,t,o);const l=o.map(e=>a[e]);"board"===t?e.state.tasks=l:e.state.archive=l}return{mode:n,scope:i}},toggleSubtask(e,t){const n=gt(e,t.id);ft(e,n,t);const a=n.task;let s=Number.isInteger(t.index)?t.index:a.subtasks.findIndex(e=>e.text===(t.text||"").trim());a.subtasks[s]||Xe("unknown_subtask",`${a.id} has no such subtask`,{details:{count:a.subtasks.length}});const r=void 0===t.completed?!a.subtasks[s].completed:!!t.completed;if(a.subtasks[s].completed!==r){const t=Ze(a);a.subtasks[s].completed=r,vt(e,n,t,a)}return{id:a.id,kind:n.kind}},addSubtask(e,t){const n=gt(e,t.id);ft(e,n,t);const[a]=nt({subtasks:[{text:t.text,completed:t.completed}]},e.state.config,{partial:!0}).subtasks;n.task.subtasks.push(a);try{!function(e,t,n){const a=we(e,t);if(a.subHeaders.length>1)throw new ie("several Subtasks sections");a.subItems.length?Be(e,a.subItems[a.subItems.length-1].line+1,[Ae(n)]):a.subHeaders.length?Be(e,a.subHeaders[0]+1,[Ae(n)]):Ie(e,t,a,[n])}(n.doc,kt(e,n),a)}catch(t){if(!(t instanceof ie))throw t;De(n.doc,kt(e,n),n.task)}return{id:n.task.id,kind:n.kind}},appendNote(e,t){const n=gt(e,t.id);ft(e,n,t);const a=nt({notes:t.text},e.state.config,{partial:!0}).notes;a||Xe("invalid_field","note text must not be empty");const s=n.task,r=a.replace(/\n/g,n.doc.eol),o=s.notes&&function(e,t){const n=we(e,t);return l(e.lines[n.lastContent])||e.eol}(n.doc,kt(e,n));s.notes=s.notes?`${s.notes}${o}${r}`:r;try{!function(e,t,n){const a=we(e,t),s=n.split(/\r?\n/);a.notes>=0&&a.lastContent>a.notes?Be(e,a.lastContent+1,s):Le(e,t,n)}(n.doc,kt(e,n),a)}catch(t){if(!(t instanceof ie))throw t;De(n.doc,kt(e,n),s)}return{id:s.id,kind:n.kind}},archiveTask(e,t){const n=gt(e,t.id);"archive"===n.kind&&Xe("already_archived",`${n.task.id} is already archived`),ft(e,n,t);const a=n.task,s=pt(e);Et(e,n,a.status);const r=Tt(He(n.doc,kt(e,n)),a,n.doc,s);return Re(s,s.lines.length,r),rt(e,n.kind,st(e,n.kind).filter(e=>e!==a)),e.state.archive.push(a),e.moves.push({from:n.kind,to:"archive"}),{id:a.id,kind:"archive"}},archiveTasks(e,t){const n=[...new Set((Array.isArray(t.ids)?t.ids:[]).map(String))];n.length||Xe("missing_field",'archiveTasks needs a non-empty "ids" array');for(const t of n)Bt.archiveTask(e,{type:"archiveTask",id:t});return{ids:n,count:n.length,kind:"archive"}},restoreTask(e,t){const n=gt(e,t.id);"archive"!==n.kind&&Xe("not_archived",`${n.task.id} is not archived`);const a=n.task,s=e.state.config.columns.some(e=>e.id===a.status)?a.status:e.state.config.columns[0].id;Et(e,n,s);const r=Tt(He(e.archive,kt(e,n)),a,e.archive,e.board);return Re(e.board,e.board.lines.length,r),a.status=s,e.state.archive=e.state.archive.filter(e=>e!==a),e.state.tasks.push(a),e.moves.push({from:"archive",to:"board"}),{id:a.id,kind:"board"}},deleteTask(e,t){const n=gt(e,t.id);return ft(e,n,t),He(n.doc,kt(e,n)),rt(e,n.kind,st(e,n.kind).filter(e=>e!==n.task)),{id:n.task.id,kind:n.kind}},setBacklogFile(e,t){const n=e.state.config;if(!1===t.enabled){if(!n.backlogFile)return{name:"backlogFile"};n.backlogFile=null;for(const t of[...e.state.backlog])Et(e,{task:t,kind:"backlog",doc:e.backlog},t.status),ct(e,t,"backlog","board");return ze(e.board,"backlogFile"),{name:"backlogFile"}}const a=n.columns.map(e=>e.id),s=n.backlogFile||{},r=t.file||s.file||E;A(r)||Xe("invalid_field",`Invalid backlog file name "${r}"`,{hint:"A plain *.md name, not kanban.md or archive.md."});const o=t.column||s.column||L({columns:n.columns,backlogFile:{file:r}})?.column;return o||Xe("missing_field","Which column is the backlog? Pass column.",{valid:a}),a.includes(o)||Xe("unknown_status",`Unknown column "${o}"`,{valid:a}),e.state.backlog.length&&r!==s.file&&Xe("backlog_file_in_use",`${s.file} still holds ${e.state.backlog.length} task(s)`,{hint:"Disable the backlog file first (enabled: false), then enable it under the new name."}),e.backlogName=r,it(e),n.backlogFile={file:r,column:o},je(e.board,"backlogFile",F(n.backlogFile),Y(n)),{name:"backlogFile"}},setHousekeeping(e,t){const n=e.state.config.columns.map(e=>e.id);if(t.reset)return e.state.config.housekeeping=null,ze(e.board,"housekeeping"),{name:"housekeeping"};const a=t.off?{enabled:!1}:{enabled:!0},s=e=>{const s=t[e];void 0!==s&&("object"==typeof s&&null!==s||Xe("invalid_field",`${e} must be { column, afterDays }`),void 0===s.column||n.includes(s.column)||Xe("unknown_status",`Unknown column "${s.column}"`,{valid:n}),void 0!==s.afterDays&&(!Number.isInteger(s.afterDays)||s.afterDays<1)&&Xe("invalid_field",`${e}.afterDays must be a whole number of days, 1 or more`),a[e]={...void 0!==s.column&&{column:s.column},...void 0!==s.afterDays&&{afterDays:s.afterDays}})};t.off||(s("done"),s("backlog"),void 0!==t.max&&((!Number.isInteger(t.max)||t.max<1)&&Xe("invalid_field","max must be a whole number, 1 or more"),a.max=t.max));const r=function(e){return B(T(e))||null}(a);return e.state.config.housekeeping=r,je(e.board,"housekeeping",T(r),Y(e.state.config)),{name:"housekeeping"}},setConfigList(e,t){Ge.includes(t.name)||Xe("unknown_config",`Unknown config list "${t.name}"`,{valid:Ge}),Array.isArray(t.values)||Xe("invalid_field","values must be an array");let n=t.values.map(e=>et(e,t.name)).filter(Boolean);if("tags"===t.name){n=n.map(e=>e.replace(/^#/,""));for(const e of n)/^[\w-]+$/.test(e)||Xe("invalid_tag",`Invalid tag "${e}"`)}return["categories","users"].includes(t.name)&&n.some(e=>e.includes(","))&&Xe("invalid_field",`${t.name} must not contain ","`),"priorities"===t.name&&n.some(e=>e.includes("|"))&&Xe("invalid_field",'priorities must not contain "|"'),"priorities"!==t.name&&(n=[...new Set(n)]),n.length||Xe("invalid_field",`${t.name} must not be empty`,{hint:"An empty list falls back to defaults, so it would not stay empty."}),e.state.config[t.name]=n,je(e.board,t.name,Ke[t.name](n),Y(e.state.config)),{name:t.name}},setColumns(e,t){Array.isArray(t.columns)&&t.columns.length||Xe("invalid_field","columns must be a non-empty array of { name, id }");const n=new Map(e.state.config.columns.map(e=>[e.id,e])),a=t.columns.map(e=>({name:et(e.name,"column name"),id:et(e.id,"column id")})),s=new Set;for(const e of a){const t=n.get(e.id);t&&t.name===e.name||(!e.name||/[|]/.test(e.name)||/\)\s*$/.test(e.name))&&Xe("invalid_column",`Invalid column name "${e.name}"`,{hint:'Names must not contain "|" or end with ")".'}),t||/^[\w-]+$/.test(e.id)||Xe("invalid_column",`Invalid column id "${e.id}"`,{hint:'Ids are letters, digits, "_" and "-".'}),s.has(e.id)&&Xe("invalid_column",`Duplicate column id "${e.id}"`),s.add(e.id)}const r=t.reassign||{};for(const e of Object.values(r))s.has(e)||Xe("invalid_column",`reassign target "${e}" is not a column`);const o=e.state.tasks.filter(e=>n.has(e.status)&&!s.has(e.status)&&!r[e.status]).map(e=>({id:e.id,status:e.status}));o.length&&Xe("orphaned_tasks",`${o.length} task(s) are in columns being removed`,{details:{orphans:o},hint:"Pass reassign: { oldColumnId: newColumnId }."});for(const t of e.state.tasks)r[t.status]&&!s.has(t.status)&&$t(e,"board",t,r[t.status]);for(const t of e.state.archive)r[t.status]&&!s.has(t.status)&&$t(e,"archive",t,r[t.status]);for(const t of e.state.backlog)r[t.status]&&!s.has(t.status)&&$t(e,"backlog",t,r[t.status]);const i=e.state.config.backlogFile,l=L(e.state.config);return l&&!s.has(l.column)&&(r[l.column]&&!n.has(r[l.column])||Xe("backlog_column_in_use",`The backlog file holds the tasks of column "${l.column}"`,{hint:"Disable the backlog file first (setBacklogFile enabled: false), or rename the column (reassign to a new id)."}),e.state.config.backlogFile={file:i.file,column:r[l.column]},je(e.board,"backlogFile",F(e.state.config.backlogFile),Y(e.state.config))),e.state.config.columns=a,je(e.board,"columns",Ke.columns(a),Y(e.state.config)),{name:"columns"}}};function Tt(e,t,n,a){if(!e.some(e=>{const t=(/\r?\n$/.exec(e)||[""])[0];return t&&t!==a.eol})||n.eol===a.eol&&!(new Set(a.lines.map(l).filter(Boolean)).size<=1))return e;const s=e=>e.replace(/\r?\n/g,a.eol);return t.notes=s(t.notes),t.extra=s(t.extra),Me(e,a.eol)}function $t(e,t,n,a){const s=e[t];Ce(s,ve(s,t,n.id),"status",a),n.status=a}function Et(e,t,n){const a=kt(e,t);(function(e,t,n){const a=ue.find(([e])=>e===n)[1],s=we(e,t).fields[a];return s?s.value:null})(t.doc,a,"status")!==n&&Ce(t.doc,a,"status",n)}function Ct(e,t,n={}){t&&Bt[t.type]||Xe("unknown_op",`Unknown operation "${t&&t.type}"`,{valid:We});const a={today:n.today||Ye},s=function(e){const t=P(e.board);t.formatVersion<re&&Xe("needs_migration",`kanban.md uses format v${t.formatVersion}; edits need v${re}`,{hint:"Run `kanbanmd migrate` (it keeps a backup and refuses anything that would not read back identically), or open the board in the app."});const n="string"==typeof e.archive,a=n?q(e.archive,t.config.columns):{tasks:[],formatVersion:re},s=L(t.config),r="string"==typeof e.backlog,o=s&&r?U(e.backlog,t.config.columns,s.column):{tasks:[]};return{board:new oe(e.board),archive:n?new oe(e.archive):null,backlog:s&&r?new oe(e.backlog):null,backlogName:t.config.backlogFile&&t.config.backlogFile.file||E,strayBacklog:!s&&r?e.backlog:null,archiveVersion:a.formatVersion,moves:[],state:{config:Ze(t.config),tasks:Ze(t.tasks),archive:Ze(a.tasks),backlog:Ze(o.tasks)},origin:e}}(e),r=Bt[t.type](s,t,a);dt(s);const o={board:e.board,archive:e.archive??null,backlog:e.backlog??null},i={board:s.board.toString(),archive:s.archive?s.archive.toString():o.archive,backlog:s.backlog?s.backlog.toString():o.backlog};!function(e,t){const n=P(t.board),a=p({config:e.state.config,tasks:e.state.tasks},{config:n.config,tasks:n.tasks});(n.formatVersion<re||a)&&Xe("verify_failed","The change could not be written so that it reads back identically; nothing was written.",{details:a||{path:"formatVersion"}});if(null!==t.archive){const a=q(t.archive,n.config.columns),s=p(e.state.archive,a.tasks);s&&Xe("verify_failed","The archive change could not be written so that it reads back identically; nothing was written.",{details:{file:"archive.md",...s}})}const s=L(e.state.config);if(s){const a=null===t.backlog?{tasks:[]}:U(t.backlog,n.config.columns,s.column),r=p(e.state.backlog,a.tasks);r&&Xe("verify_failed","The backlog change could not be written so that it reads back identically; nothing was written.",{details:{file:s.file,...r}});const o=e.state.tasks.find(e=>e.status===s.column)||e.state.backlog.find(e=>e.status!==s.column);o&&Xe("verify_failed",`${o.id} is in the wrong file for its status; nothing was written.`,{details:{id:o.id,status:o.status}})}}(s,i);const l=Object.fromEntries(at.map(e=>[e,i[e]!==o[e]])),c=ut(at.filter(e=>l[e]),s.moves).map(e=>({name:ot(s,e),kind:e,text:i[e]}));let u,h=r.kind;if(r.id){const e=at.find(e=>st(s,e).some(e=>e.id===r.id));if(e&&s[e]){h=e;const t=ve(s[e],e,r.id);t&&(u=d(ye(s[e],t)))}}return{...i,files:c,changed:l,moves:s.moves,result:{op:t.type,...r,kind:h,etag:u}}}function At(){const e=new Date;return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}function It(e){const t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e||"");if(!t)return null;const n=Date.UTC(+t[1],+t[2]-1,+t[3]),a=new Date(n);return a.getUTCFullYear()===+t[1]&&a.getUTCMonth()===+t[2]-1&&a.getUTCDate()===+t[3]?n/864e5:null}var Ft=20;function Lt(e){const t=P(e.board),n=L(t.config),a=n&&"string"==typeof e.backlog?U(e.backlog,t.config.columns,n.column).tasks:[];return{bp:t,bf:n,live:[...t.tasks,...a]}}function Dt(e,t,n,a){const s=It(a),r=e=>{const t=It(e);return null===t||null===s?null:s-t},o=n.done.column?e.tasks.filter(e=>e.status===n.done.column):[],i=o.filter(e=>e.completed&&r(e.completed)>=n.done.afterDays).sort((e,t)=>e.completed.localeCompare(t.completed)),l=n.backlog.column?t.filter(e=>e.status===n.backlog.column):[],c=l.filter(e=>{const t=e.created?r(e.created):null,a=e.due&&!e.completed?r(e.due):null;return null!==t&&t>=n.backlog.afterDays||null!==a&&a>=n.backlog.afterDays}).sort((e,t)=>(e.created||"").localeCompare(t.created||""));return{done:o,doneOld:i,backlog:l,backlogStale:c}}function Ot(e,{today:t=At()}={}){const{bp:n,bf:a,live:s}=Lt(e),r=$(n.config),o={policy:r,suggestions:[]};if(!r.enabled)return o;const i=Dt(n,s,r,t),l=e=>e.slice(0,Ft).map(e=>e.id);if(i.doneOld.length){const e=i.done.filter(e=>!e.completed).length;o.suggestions.push({kind:"archive_done",count:i.doneOld.length,afterDays:r.done.afterDays,ids:l(i.doneOld),...e&&{undated:e},message:`${i.doneOld.length} task(s) in "${r.done.column}" were finished ${r.done.afterDays}+ days ago: archive them to keep the board small.`})}return i.backlogStale.length&&o.suggestions.push({kind:"stale_backlog",count:i.backlogStale.length,afterDays:r.backlog.afterDays,ids:l(i.backlogStale),message:`${i.backlogStale.length} task(s) in "${r.backlog.column}" are ${r.backlog.afterDays}+ days old: review, archive or delete them.`}),!a&&i.backlog.length>r.backlogFileAbove&&o.suggestions.push({kind:"backlog_large",count:i.backlog.length,max:r.backlogFileAbove,column:r.backlog.column,message:`${i.backlog.length} tasks in "${r.backlog.column}" (limit ${r.backlogFileAbove}): move them to their own backlog file to keep the board small.`}),n.tasks.length>r.max&&o.suggestions.push({kind:"board_large",count:n.tasks.length,max:r.max,message:`${n.tasks.length} tasks on the board (limit ${r.max}): archive what is finished.`}),o}function Pt(e,{today:t=At()}={}){const{bp:n,live:a}=Lt(e),s=$(n.config);return s.enabled?Dt(n,a,s,t).backlogStale.map(e=>e.id):[]}function Mt(e,{today:t=At()}={}){const{bp:n,live:a}=Lt(e),s=$(n.config);return s.enabled?Dt(n,a,s,t).doneOld.map(e=>e.id):[]}var Ht=["title","status","order","priority","category","assignees","tags","blockedBy","created","started","due","completed","description","subtasks","notes"],Rt=["categories","users","priorities","tags"],Nt=e=>new Map(e.map(e=>[e.id,e]));function _t(e,t){const n=[],a=[],s=Nt([...e.tasks,...e.backlog||[]]),r=Nt(e.archive),o=Nt([...t.tasks,...t.backlog||[]]),i=Nt(t.archive),l=e.config.columns.map(e=>e.id),c=t.config.columns.map(e=>e.id);for(const e of[...s.keys(),...r.keys()])o.has(e)||i.has(e)||n.push({type:"deleteTask",id:e});for(const e of s.keys())i.has(e)&&n.push({type:"archiveTask",id:e});for(const e of r.keys())o.has(e)&&n.push({type:"restoreTask",id:e});const d={},p=!u(e.config.columns,t.config.columns),h=new Set;if(p){for(const e of l.filter(e=>!c.includes(e))){const t=new Map,n=(e,n)=>t.set(n,[...t.get(n)||[],e]);for(const[t,a]of s){if(a.status!==e)continue;const s=o.get(t)||i.get(t);s&&(i.has(t)&&!c.includes(s.status)||n(t,c.includes(s.status)?s.status:c[0]))}for(const[t,a]of r){if(a.status!==e)continue;const s=i.get(t)||o.get(t);s&&c.includes(s.status)&&n(t,s.status)}if(!t.size)continue;const[a,l]=[...t.entries()].sort((e,t)=>t[1].length-e[1].length)[0];d[e]=a;for(const e of l)h.add(e)}n.push({type:"setColumns",columns:t.config.columns.map(e=>({name:e.name,id:e.id})),...Object.keys(d).length&&{reassign:d}})}const m=(e,t)=>{const n={};for(const a of Ht)"status"===a&&h.has(t.id)||u(e[a],t[a])||(n[a]="order"===a&&null===t[a]?"":t[a]);return u(e.extra,t.extra)||a.push(`${t.id}: preserved sections (Links/Review/Dependencies) cannot be edited`),n};for(const[e,a]of[...o,...i]){const i=s.get(e)||r.get(e);if(!i)continue;let l=i;if(r.has(e)&&o.has(e)){const e=t.config.columns.some(e=>e.id===i.status)?i.status:t.config.columns[0].id;l={...i,status:e}}const c=m(l,a);Object.keys(c).length&&n.push({type:"updateTask",id:e,set:c})}for(const[e,t]of o){if(s.has(e)||r.has(e))continue;const a={};for(const e of Ht){const n=t[e];("order"===e?null===n:""===n||Array.isArray(n)&&!n.length)||(a[e]=n)}n.push({type:"createTask",task:a,clientId:e})}for(const e of i.keys())r.has(e)||s.has(e)||a.push(`${e}: a task appeared directly in the archive`);for(const a of Rt)u(e.config[a],t.config[a])||n.push({type:"setConfigList",name:a,values:t.config[a]});return e.config.boardTitle!==t.config.boardTitle&&a.push("the board title changed"),u(e.config.housekeeping,t.config.housekeeping)||a.push("the housekeeping settings changed"),{ops:n,unsupported:a}}function jt(e,t,n={}){const a={board:e.board,archive:e.archive??null,backlog:e.backlog??null};let s={...a};const r=[],o=[],i={};for(let e=0;e<t.length;e++){const{clientId:a,...l}=t[e];let c;try{c=Ct(s,l,n)}catch(t){throw t.opIndex=e,t}for(const e of c.files)i[e.kind]=e.name;o.push(...c.moves),s={board:c.board,archive:c.archive,backlog:c.backlog},r.push({...c.result,...a&&{clientId:a}})}const l=ut(Object.keys(i).filter(e=>s[e]!==a[e]),o);return{...s,results:r,files:l.map(e=>({name:i[e],kind:e,text:s[e]}))}}var zt,Kt=e=>String(e).replace(/\r\n/g,"\n");function qt(e,t=[],{notes:n=!0,assignees:a=!0}={}){const s=t.find(t=>t.id===e.status),r=[e.priority,e.category,s?s.name:e.status,e.due&&`Due ${e.due}`,a&&e.assignees.length&&`Assigned ${e.assignees.join(", ")}`].filter(Boolean).join(" · "),o=[e.title];if(r&&o.push(r),e.tags.length&&o.push(e.tags.join(" ")),e.description&&o.push("",Kt(e.description)),e.subtasks.length){const t=e.subtasks.filter(e=>e.completed).length;o.push("",`Subtasks (${t}/${e.subtasks.length}):`,...e.subtasks.map(e=>`${e.completed?"[x]":"[ ]"} ${e.text}`))}return n&&e.notes&&o.push("","Notes:",Kt(e.notes)),o.join("\n")+"\n"}function Ut(e,{notes:t=!0,assignees:n=!0}={}){return Kt(Q({...e,assignees:n?e.assignees:[],notes:t?e.notes:"",extra:t?e.extra:"",order:null,blockedBy:[]})).trimEnd()+"\n"}function Vt(e,t){const n=String(e??"").trim();if(!n)return null;let a;try{a=new URL(n,t)}catch(e){return null}if("https:"!==a.protocol&&"http:"!==a.protocol)return null;const s="github.com"===a.hostname&&/^\/([^/]+)\/([^/]+)\/blob\/(.+)$/.exec(a.pathname);s&&(a=new URL(`https://raw.githubusercontent.com/${s[1]}/${s[2]}/${s[3]}`)),a.pathname.endsWith("/")&&(a.pathname+="kanban.md"),a.hash="";let r=null;try{r=new URL(t).origin}catch(e){}return{href:a.href,origin:a.origin,sameOrigin:a.origin===r,label:a.host+a.pathname}}function Wt(e,t){return!/^\w[\w.-]*\.md$/.test(String(t))||t.includes("..")?null:new URL(t,e).href}function Gt(e){const t=String(e??"");return/<!-- Config: Last Task ID: \d+ -->/.test(t)||/^\*\*Columns\*\*:/m.test(t)||/^###\s+TASK-\d+\s*\|/m.test(t)}return zt=s,((s,r,o,i)=>{if(r&&"object"==typeof r||"function"==typeof r)for(let l of n(r))a.call(s,l)||l===o||e(s,l,{get:()=>r[l],enumerable:!(i=t(r,l))||i.enumerable});return s})(e({},"__esModule",{value:!0}),zt)})();const{parseBoard:parseBoard,parseArchive:parseArchive,generateBoard:generateBoard,generateArchive:generateArchive,generateTaskBlock:generateTaskBlock,normalizeConfig:normalizeConfig,createDefaultBoardContent:createDefaultBoardContent,createDefaultArchiveContent:createDefaultArchiveContent,orderedTasks:orderedTasks,hasSourceComment:hasSourceComment,diffToOps:diffToOps,applyOps:applyOps,deepEqual:deepEqual,getHousekeeping:getHousekeeping,archiveCandidates:archiveCandidates,staleBacklogIds:staleBacklogIds,parseHousekeepingSpec:parseHousekeepingSpec,formatHousekeepingSpec:formatHousekeepingSpec,resolveBacklogFile:resolveBacklogFile,parseBacklog:parseBacklog,backlogFileNameOf:backlogFileNameOf,DEFAULT_BACKLOG_STRINGS:DEFAULT_BACKLOG_STRINGS,taskToPlainText:taskToPlainText,taskToMarkdown:taskToMarkdown,indexTasks:indexTasks,blockersOf:blockersOf,blockedByThis:blockedByThis,hasCycle:hasCycle,isFinished:isFinished,parseTaskIds:parseTaskIds,resolveBoardUrl:resolveBoardUrl,boardFileUrl:boardFileUrl,looksLikeBoard:looksLikeBoard}=KanbanCore,THEME_ICONS={auto:'<svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="5.5"/><path d="M8 2.5v11" /><path d="M8 2.5a5.5 5.5 0 0 0 0 11z" fill="currentColor"/></svg>',light:'<svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="3"/><path d="M8 1.5v1.5M8 13v1.5M1.5 8H3M13 8h1.5M3.4 3.4l1 1M11.6 11.6l1 1M3.4 12.6l1-1M11.6 4.4l1-1"/></svg>',dark:'<svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7z"/></svg>'},themeQuery=window.matchMedia("(prefers-color-scheme: dark)");let themeMode=function(){try{const e=localStorage.getItem("theme");return"light"===e||"dark"===e?e:"auto"}catch(e){return"auto"}}();function resolveTheme(){return"auto"===themeMode?themeQuery.matches?"dark":"light":themeMode}function updateThemeUi(){const e=document.getElementById("themeMenuBtn");if(!e)return;e.innerHTML=THEME_ICONS[themeMode],e.title=t("menu.theme"),e.setAttribute("aria-label",t("menu.theme"));const n={auto:t("menu.themeAuto"),light:t("menu.themeLight"),dark:t("menu.themeDark")};document.querySelectorAll("[data-theme-mode]").forEach(e=>{const t=e.dataset.themeMode;e.querySelector(".lbl").textContent=n[t],e.querySelector(".check").textContent=t===themeMode?"✓":"",e.setAttribute("aria-checked",t===themeMode?"true":"false")})}function setThemeMode(e){themeMode=e;try{"auto"===e?localStorage.removeItem("theme"):localStorage.setItem("theme",e)}catch(e){}document.documentElement.setAttribute("data-theme",resolveTheme()),updateThemeUi(),"function"==typeof closeMenus&&closeMenus()}themeQuery.addEventListener("change",()=>{"auto"===themeMode&&document.documentElement.setAttribute("data-theme",resolveTheme())}),document.documentElement.setAttribute("data-theme",resolveTheme());let source=null,currentKanbanContent="",tasks=[],config={},activeFilters=[],globalSearchTerm="",isEditMode=!1,currentDetailTask=null,formSubtasks=[],archivedTasks=[],detailCtx={archived:!1,fromArchivePanel:!1},detailStack=[],loadedFormatVersion=3,loadedArchiveVersion=3,currentLanguage="en";const translations={en:{"page.title":"Kanban.md","header.title":"Kanban.md","header.renameProject":"Rename project","header.deleteProject":"Remove project from list","header.folder":"Open folder…","header.folderAnother":"Open another folder…","header.folderReadOnly":"Open folder (read-only)…","header.backToService":"Back to served board","header.openUrl":"Open from a web address…","prompt.boardUrl":"Address of a board (its kanban.md file or folder, or a GitHub link):","welcome.openUrl":"Or open a board from a web address","header.home":"Kanban.md website","header.source":"Source on GitHub","header.newTask":"Task","header.archives":"Archives","header.columns":"Columns","menu.language":"Language","menu.theme":"Theme","menu.themeAuto":"Auto","menu.themeLight":"Light","menu.themeDark":"Dark","filters.title":"Filters","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":"Editing a folder needs Chrome 86+, Edge 86+ or Opera 72+. In other browsers (Firefox, Safari) you can open a folder or a web address read-only.","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","links.blockedBy":"Blocked by","links.back":"Back to {id}","links.noMatch":'No task matches "{q}". Archived tasks, this task and ones already listed are left out.',"links.severalMatches":'Several tasks match "{q}". Pick one from the list.',"links.blocks":"Blocks","links.add":"Add a task: id or title","links.help":"Pick from the list. A task waiting on others shows a lock on its card.","links.badge":"Open blockers ({count}): {list}","links.inBacklog":"In backlog","links.backlogNote":"{id} is in the backlog.","links.archived":"Archived","links.moveToBoard":"Move to board","links.unknown":"Unknown task","links.cycle":"These tasks wait on each other.","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.reloaded":"Board updated from disk","share.button":"Share","share.title":"Share task","share.text":"Plain text","share.markdown":"Markdown","share.includeNotes":"Include notes","share.includeAssignees":"Include assignees","share.copy":"Copy","share.share":"Share…","share.copied":"Copied to the clipboard","share.copyFailed":"Could not copy: select the text and copy it yourself","share.hint":"Only what you see above is shared, and nothing leaves your browser until you send it. Markdown is the board file format, so another board can import it.","web.readOnly":"Read-only","picked.edit":"Edit with kanbanmd","picked.editHint":'To edit this board in any browser, run "kanbanmd serve" in the folder (needs Node). Click to copy the command.',"web.error.noBoard":"There is no kanban.md in this folder.","picked.opened":"Opened {name}","picked.noteTitle":"Opening a folder in this browser","picked.noteText":'Your browser will call this an "Upload". **Nothing is sent anywhere.** Kanban.md only reads the .md files in the folder you pick, and only inside this tab.',"picked.noteContinue":"Continue","notif.loadError":"Could not load the board","status.served":"Served board","status.stopped":'kanbanmd stopped: changes are not saved. Run "kanbanmd serve" again and open its address.',"status.expired":"Session expired: open the new address kanbanmd serve printed.","status.synced":"Synced {time}","serveHelp.title":"Edit this folder in your browser","serveHelp.intro":"What you see is a read-only copy. To edit the real files, serve the folder with kanbanmd (needs Node 18 or newer).","serveHelp.step1":"Open a terminal in the folder you picked.","serveHelp.step2":"Run this command:","serveHelp.step3":"Your browser opens the board, and changes are saved to the real files.","serveHelp.copy":"Copy","serveHelp.copied":"Copied","serveHelp.close":"Close","status.read":"Read {time}","status.readRefresh":"Read at {time}. Refresh.","status.refreshed":"Board updated","status.upToDate":"Already up to date","status.refreshFailed":"Couldn't refresh the board. Check your connection and try again.","picked.refreshTitle":"Refresh this folder","picked.refreshText":"Browsers can’t re-read a folder on their own. To refresh, choose **{name}** again. If you choose a different folder, that board opens instead.","picked.refreshContinue":"Choose folder","picked.refreshServe":"To update automatically, use **kanbanmd serve**.","picked.refreshHow":"How?","web.confirmTitle":"Open a board from another site?","web.confirmText":"The board will be downloaded from {host} and shown read-only. Nothing is saved, and nothing is sent to that site except the request for the files.","web.confirmOpen":"Open board","web.cancel":"Cancel","web.errorTitle":"Could not open this board","web.error.invalid":"The board address must be a web address (http or https).","web.error.notFound":"There is no kanban.md at this address.","web.error.network":"The board could not be downloaded. You may be offline, or the site does not let other pages read its files. Raw GitHub links and GitHub Pages work.","web.error.http":"The site answered with an error ({status}).","web.error.tooLarge":"The file is too large to open (over 5 MB).","web.error.notBoard":"This file is not a Kanban.md board.","hk.menu":"Housekeeping","hk.backlog_large":"{count} tasks in the backlog (limit {max})","hk.splitBtn":"Move backlog to its own file","backlog.title":"Backlog","backlog.search":"Search backlog...","backlog.empty":"Nothing in the backlog","backlog.open":"Open backlog","archive.open":"Open archive","rail.hint":"Click to open","archive.stub":"Kept in archive.md","backlog.stub":"Kept in {file}","backlog.moveTo":"Move to {name}","backlog.archive":"Archive","backlog.old":"old","backlog.menuOn":"Backlog file: on","backlog.menuOff":"Backlog file: off","backlog.openMenu":"Open backlog","backlog.confirmOn":'Move the {count} task(s) in "{column}" into backlog.md?\n\nThey stay one click away in the Backlog panel, and kanban.md gets smaller.',"backlog.confirmOff":"Bring the {count} backlog task(s) back into kanban.md?","backlog.askColumn":"Which column is the backlog? ({ids})","backlog.onDone":"The backlog is now in its own file","backlog.offDone":"The backlog is back in kanban.md","hk.archive_done":"{count} done task(s) finished {days}+ days ago","hk.stale_backlog":"{count} backlog task(s) older than {days} days (outlined)","hk.board_large":"{count} tasks on the board (limit {max})","hk.archiveBtn":"Archive done ({count})","hk.snooze":"Snooze 7 days","hk.dismiss":"Dismiss","hk.confirmArchive":"Archive {count} finished task(s)?\n\nThey move to archive.md in one step and can be restored from Archives.","hk.archived":"{count} task(s) archived","hk.staleCard":"Old backlog task: review, archive or delete it","hk.settingsPrompt":'Housekeeping policy. Example: done=done after=30d | backlog=backlog after=90d | max=200\n"off" turns the reminders off, "reset" restores the defaults.',"hk.saved":"Housekeeping settings saved","menu.manualOrderOn":"Manual order: on","menu.manualOrderOff":"Manual order: off","menu.resetOrderById":"Reset order by task id","confirm.rememberOrder":"Remember the order of your cards?\n\nOK: store a number on each task so this order is kept exactly (one-time change to every task, then each move changes two lines).\nCancel: just move the card in the file.","confirm.orderOff":"Turn manual order off?\n\nThe Order line is removed from every task and cards follow their position in the file.","confirm.resetOrder":"Reset the order of all cards to task id order?","notif.orderOn":"Manual order is on","notif.orderOff":"Manual order is off","notif.orderReset":"Order reset to task id","notif.saveFailed":"Could not save the change","notif.mergeFailed":"The board changed on disk and your change could not be merged ({reason}). The latest version was loaded.","notif.taskRemovedElsewhere":"Task {id} was removed outside this window.","confirm.taskChangedElsewhere":"Task {id} was changed outside this window.\n\nOK: load the new values into the form.\nCancel: keep what you typed (it will replace the other change).","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.',"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":"Kanban.md","header.title":"Kanban.md","header.renameProject":"Renommer le projet","header.deleteProject":"Retirer le projet de la liste","header.folder":"Ouvrir un dossier…","header.folderAnother":"Ouvrir un autre dossier…","header.folderReadOnly":"Ouvrir un dossier (lecture seule)…","header.backToService":"Retour au tableau servi","header.openUrl":"Ouvrir depuis une adresse web…","prompt.boardUrl":"Adresse d'un tableau (son fichier kanban.md ou son dossier, ou un lien GitHub) :","welcome.openUrl":"Ou ouvrir un tableau depuis une adresse web","header.home":"Site de Kanban.md","header.source":"Code source sur GitHub","header.newTask":"Tâche","header.archives":"Archives","header.columns":"Colonnes","menu.language":"Langue","menu.theme":"Thème","menu.themeAuto":"Auto","menu.themeLight":"Clair","menu.themeDark":"Sombre","filters.title":"Filtres","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":"Modifier un dossier nécessite Chrome 86+, Edge 86+ ou Opera 72+. Dans les autres navigateurs (Firefox, Safari), vous pouvez ouvrir un dossier ou une adresse web en lecture seule.","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","links.blockedBy":"Bloquée par","links.back":"Retour à {id}","links.noMatch":"Aucune tâche ne correspond à « {q} ». Les tâches archivées, cette tâche et celles déjà listées sont exclues.","links.severalMatches":"Plusieurs tâches correspondent à « {q} ». Choisissez dans la liste.","links.blocks":"Bloque","links.add":"Ajouter une tâche : identifiant ou titre","links.help":"Choisissez dans la liste. Une tâche qui en attend d'autres porte un cadenas sur sa carte.","links.badge":"Bloquants ouverts ({count}) : {list}","links.inBacklog":"Dans le backlog","links.backlogNote":"{id} est dans le backlog.","links.archived":"Archivée","links.moveToBoard":"Mettre sur le tableau","links.unknown":"Tâche inconnue","links.cycle":"Ces tâches s'attendent mutuellement.","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.reloaded":"Tableau mis à jour depuis le disque","share.button":"Partager","share.title":"Partager la tâche","share.text":"Texte brut","share.markdown":"Markdown","share.includeNotes":"Inclure les notes","share.includeAssignees":"Inclure les assignés","share.copy":"Copier","share.share":"Partager…","share.copied":"Copié dans le presse-papiers","share.copyFailed":"Copie impossible : sélectionnez le texte et copiez-le vous-même","share.hint":"Seul ce que vous voyez ci-dessus est partagé, et rien ne quitte votre navigateur avant que vous l'envoyiez. Le Markdown est le format de fichier du tableau : un autre tableau peut l'importer.","web.readOnly":"Lecture seule","picked.edit":"Modifier avec kanbanmd","picked.editHint":"Pour modifier ce tableau dans n’importe quel navigateur, lancez « kanbanmd serve » dans le dossier (Node requis). Cliquez pour copier la commande.","web.error.noBoard":"Ce dossier ne contient pas de kanban.md.","picked.opened":"Ouvert : {name}","picked.noteTitle":"Ouvrir un dossier dans ce navigateur","picked.noteText":"Votre navigateur parlera d’un « upload ». **Rien n’est envoyé nulle part.** Kanban.md lit seulement les fichiers .md du dossier choisi, et seulement dans cet onglet.","picked.noteContinue":"Continuer","notif.loadError":"Impossible de charger le tableau","status.served":"Tableau servi","status.stopped":"kanbanmd arrêté : les modifications ne sont pas enregistrées. Relancez « kanbanmd serve » et ouvrez son adresse.","status.expired":"Session expirée : ouvrez la nouvelle adresse affichée par kanbanmd serve.","status.synced":"Synchronisé à {time}","serveHelp.title":"Modifier ce dossier dans votre navigateur","serveHelp.intro":"Ce que vous voyez est une copie en lecture seule. Pour modifier les vrais fichiers, servez le dossier avec kanbanmd (Node 18 ou plus récent requis).","serveHelp.step1":"Ouvrez un terminal dans le dossier choisi.","serveHelp.step2":"Lancez cette commande :","serveHelp.step3":"Votre navigateur ouvre le tableau, et les modifications sont enregistrées dans les vrais fichiers.","serveHelp.copy":"Copier","serveHelp.copied":"Copié","serveHelp.close":"Fermer","status.read":"Lu à {time}","status.readRefresh":"Lu à {time}. Actualiser.","status.refreshed":"Tableau mis à jour","status.upToDate":"Déjà à jour","status.refreshFailed":"Impossible d'actualiser le tableau. Vérifiez votre connexion et réessayez.","picked.refreshTitle":"Actualiser ce dossier","picked.refreshText":"Un navigateur ne peut pas relire un dossier de lui-même. Pour actualiser, choisissez à nouveau **{name}**. Si vous choisissez un autre dossier, c’est ce tableau qui s’ouvre.","picked.refreshContinue":"Choisir le dossier","picked.refreshServe":"Pour une mise à jour automatique, utilisez **kanbanmd serve**.","picked.refreshHow":"Comment ?","web.confirmTitle":"Ouvrir un tableau d'un autre site ?","web.confirmText":"Le tableau sera téléchargé depuis {host} et affiché en lecture seule. Rien n'est enregistré, et rien n'est envoyé à ce site à part la demande des fichiers.","web.confirmOpen":"Ouvrir le tableau","web.cancel":"Annuler","web.errorTitle":"Impossible d'ouvrir ce tableau","web.error.invalid":"L'adresse du tableau doit être une adresse web (http ou https).","web.error.notFound":"Il n'y a pas de kanban.md à cette adresse.","web.error.network":"Le tableau n'a pas pu être téléchargé. Vous êtes peut-être hors ligne, ou le site ne laisse pas d'autres pages lire ses fichiers. Les liens GitHub « raw » et GitHub Pages fonctionnent.","web.error.http":"Le site a répondu par une erreur ({status}).","web.error.tooLarge":"Le fichier est trop volumineux pour être ouvert (plus de 5 Mo).","web.error.notBoard":"Ce fichier n'est pas un tableau Kanban.md.","hk.menu":"Ménage","hk.backlog_large":"{count} tâches dans le backlog (limite {max})","hk.splitBtn":"Déplacer le backlog dans son propre fichier","backlog.title":"Backlog","backlog.search":"Rechercher dans le backlog...","backlog.empty":"Le backlog est vide","backlog.open":"Ouvrir le backlog","archive.open":"Ouvrir l'archive","rail.hint":"Cliquez pour ouvrir","archive.stub":"Stockées dans archive.md","backlog.stub":"Stocké dans {file}","backlog.moveTo":"Déplacer vers {name}","backlog.archive":"Archiver","backlog.old":"ancienne","backlog.menuOn":"Fichier backlog : activé","backlog.menuOff":"Fichier backlog : désactivé","backlog.openMenu":"Ouvrir le backlog","backlog.confirmOn":"Déplacer les {count} tâche(s) de « {column} » dans backlog.md ?\n\nElles restent à un clic dans le panneau Backlog, et kanban.md devient plus léger.","backlog.confirmOff":"Remettre les {count} tâche(s) du backlog dans kanban.md ?","backlog.askColumn":"Quelle colonne est le backlog ? ({ids})","backlog.onDone":"Le backlog est maintenant dans son propre fichier","backlog.offDone":"Le backlog est de retour dans kanban.md","hk.archive_done":"{count} tâche(s) terminée(s) depuis plus de {days} jours","hk.stale_backlog":"{count} tâche(s) du backlog de plus de {days} jours (contour pointillé)","hk.board_large":"{count} tâches sur le tableau (limite {max})","hk.archiveBtn":"Archiver les terminées ({count})","hk.snooze":"Reporter 7 jours","hk.dismiss":"Ignorer","hk.confirmArchive":"Archiver {count} tâche(s) terminée(s) ?\n\nElles passent dans archive.md en une seule étape et peuvent être restaurées depuis les Archives.","hk.archived":"{count} tâche(s) archivée(s)","hk.staleCard":"Ancienne tâche du backlog : à revoir, archiver ou supprimer","hk.settingsPrompt":"Règles de ménage. Exemple : done=done after=30d | backlog=backlog after=90d | max=200\n« off » désactive les rappels, « reset » rétablit les valeurs par défaut.","hk.saved":"Réglages enregistrés","menu.manualOrderOn":"Ordre manuel : activé","menu.manualOrderOff":"Ordre manuel : désactivé","menu.resetOrderById":"Réinitialiser l'ordre par identifiant","confirm.rememberOrder":"Mémoriser l'ordre de vos cartes ?\n\nOK : un numéro est stocké sur chaque tâche pour conserver cet ordre (changement unique, puis chaque déplacement modifie deux lignes).\nAnnuler : déplacer simplement la carte dans le fichier.","confirm.orderOff":"Désactiver l'ordre manuel ?\n\nLa ligne Order est retirée de chaque tâche et les cartes suivent leur position dans le fichier.","confirm.resetOrder":"Réinitialiser l'ordre de toutes les cartes selon l'identifiant ?","notif.orderOn":"Ordre manuel activé","notif.orderOff":"Ordre manuel désactivé","notif.orderReset":"Ordre réinitialisé par identifiant","notif.saveFailed":"Impossible d'enregistrer la modification","notif.mergeFailed":"Le tableau a changé sur le disque et votre modification n'a pas pu être fusionnée ({reason}). La dernière version a été chargée.","notif.taskRemovedElsewhere":"La tâche {id} a été supprimée en dehors de cette fenêtre.","confirm.taskChangedElsewhere":"La tâche {id} a été modifiée en dehors de cette fenêtre.\n\nOK : charger les nouvelles valeurs dans le formulaire.\nAnnuler : garder votre saisie (elle remplacera l'autre modification).","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".',"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":"Kanban.md","header.title":"Kanban.md","header.renameProject":"Projekt umbenennen","header.deleteProject":"Projekt aus Liste entfernen","header.folder":"Ordner öffnen…","header.folderAnother":"Anderen Ordner öffnen…","header.folderReadOnly":"Ordner öffnen (schreibgeschützt)…","header.backToService":"Zurück zum bereitgestellten Board","header.openUrl":"Von einer Webadresse öffnen…","web.readOnly":"Schreibgeschützt","web.confirmTitle":"Board von einer anderen Seite öffnen?","web.confirmText":"Das Board wird von {host} heruntergeladen und schreibgeschützt angezeigt. Es wird nichts gespeichert, und außer der Anfrage nach den Dateien wird nichts an diese Seite gesendet.","web.confirmOpen":"Board öffnen","web.cancel":"Abbrechen","web.errorTitle":"Dieses Board konnte nicht geöffnet werden","web.error.invalid":"Die Board-Adresse muss eine Webadresse sein (http oder https).","web.error.notFound":"Unter dieser Adresse gibt es keine kanban.md.","web.error.network":"Das Board konnte nicht heruntergeladen werden. Vielleicht sind Sie offline, oder die Seite erlaubt anderen Seiten nicht, ihre Dateien zu lesen. Raw-GitHub-Links und GitHub Pages funktionieren.","web.error.http":"Die Seite antwortete mit einem Fehler ({status}).","web.error.tooLarge":"Die Datei ist zu groß zum Öffnen (über 5 MB).","web.error.notBoard":"Diese Datei ist kein Kanban.md-Board.","web.error.noBoard":"In diesem Ordner gibt es keine kanban.md.","picked.opened":"{name} geöffnet","picked.noteTitle":"Ordner in diesem Browser öffnen","picked.noteText":"Ihr Browser nennt das einen „Upload“. **Es wird nichts irgendwohin gesendet.** Kanban.md liest nur die .md-Dateien des gewählten Ordners, und das nur in diesem Tab.","picked.noteContinue":"Weiter","notif.loadError":"Das Board konnte nicht geladen werden","status.served":"Bereitgestelltes Board","status.stopped":"kanbanmd beendet: Änderungen werden nicht gespeichert. Starten Sie „kanbanmd serve“ erneut und öffnen Sie dessen Adresse.","status.expired":"Sitzung abgelaufen: Öffnen Sie die neue Adresse, die kanbanmd serve ausgibt.","status.synced":"Synchronisiert um {time}","serveHelp.title":"Diesen Ordner im Browser bearbeiten","serveHelp.intro":"Sie sehen eine schreibgeschützte Kopie. Um die echten Dateien zu bearbeiten, stellen Sie den Ordner mit kanbanmd bereit (Node 18 oder neuer erforderlich).","serveHelp.step1":"Öffnen Sie ein Terminal im gewählten Ordner.","serveHelp.step2":"Führen Sie diesen Befehl aus:","serveHelp.step3":"Ihr Browser öffnet das Board, und Änderungen werden in den echten Dateien gespeichert.","serveHelp.copy":"Kopieren","serveHelp.copied":"Kopiert","serveHelp.close":"Schließen","status.read":"Gelesen um {time}","status.readRefresh":"Gelesen um {time}. Aktualisieren.","status.refreshed":"Board aktualisiert","status.upToDate":"Bereits auf dem neuesten Stand","status.refreshFailed":"Das Board konnte nicht aktualisiert werden. Prüfen Sie Ihre Verbindung und versuchen Sie es erneut.","picked.refreshTitle":"Ordner aktualisieren","picked.refreshText":"Ein Browser kann einen Ordner nicht von selbst neu einlesen. Wählen Sie zum Aktualisieren **{name}** erneut aus. Wenn Sie einen anderen Ordner wählen, wird stattdessen dieses Board geöffnet.","picked.refreshContinue":"Ordner wählen","picked.refreshServe":"Für automatische Updates nutzen Sie **kanbanmd serve**.","picked.refreshHow":"Wie?","picked.edit":"Mit kanbanmd bearbeiten","picked.editHint":"Um dieses Board in jedem Browser zu bearbeiten, führen Sie „kanbanmd serve“ im Ordner aus (Node erforderlich). Klicken zum Kopieren des Befehls.","prompt.boardUrl":"Adresse eines Boards (seine kanban.md-Datei oder sein Ordner, oder ein GitHub-Link):","welcome.openUrl":"Oder ein Board von einer Webadresse öffnen","header.home":"Kanban.md-Website","header.source":"Quellcode auf GitHub","header.newTask":"Aufgabe","header.archives":"Archiv","header.columns":"Spalten","menu.language":"Sprache","menu.theme":"Design","menu.themeAuto":"Automatisch","menu.themeLight":"Hell","menu.themeDark":"Dunkel","filters.title":"Filter","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":"Zum Bearbeiten eines Ordners braucht es Chrome 86+, Edge 86+ oder Opera 72+. Jeder Browser kann ein Board von einer Webadresse schreibgeschützt anzeigen.","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".',"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":"Kanban.md","header.title":"Kanban.md","header.renameProject":"重命名项目","header.deleteProject":"从列表中移除项目","header.folder":"打开文件夹…","header.folderAnother":"打开其他文件夹…","header.folderReadOnly":"打开文件夹(只读)…","header.backToService":"返回服务端看板","header.openUrl":"从网址打开…","web.readOnly":"只读","web.confirmTitle":"打开来自其他网站的看板?","web.confirmText":"看板将从 {host} 下载并以只读方式显示。不会保存任何内容,除了请求文件之外,也不会向该网站发送任何数据。","web.confirmOpen":"打开看板","web.cancel":"取消","web.errorTitle":"无法打开此看板","web.error.invalid":"看板地址必须是网址(http 或 https)。","web.error.notFound":"此地址下没有 kanban.md。","web.error.network":"无法下载看板。你可能已离线,或该网站不允许其他页面读取其文件。GitHub 原始链接和 GitHub Pages 可用。","web.error.http":"网站返回了错误({status})。","web.error.tooLarge":"文件太大,无法打开(超过 5 MB)。","web.error.notBoard":"此文件不是 Kanban.md 看板。","web.error.noBoard":"此文件夹中没有 kanban.md。","picked.opened":"已打开 {name}","picked.noteTitle":"在此浏览器中打开文件夹","picked.noteText":"浏览器会称之为“上传”。**不会向任何地方发送内容。** Kanban.md 只读取所选文件夹中的 .md 文件,并且只在此标签页内进行。","picked.noteContinue":"继续","notif.loadError":"无法加载看板","status.served":"服务端看板","status.stopped":"kanbanmd 已停止:更改不会被保存。请重新运行“kanbanmd serve”并打开其地址。","status.expired":"会话已过期:请打开 kanbanmd serve 输出的新地址。","status.synced":"同步于 {time}","serveHelp.title":"在浏览器中编辑此文件夹","serveHelp.intro":"你看到的是只读副本。要编辑真实文件,请用 kanbanmd 提供该文件夹(需要 Node 18 或更高版本)。","serveHelp.step1":"在你选择的文件夹中打开终端。","serveHelp.step2":"运行此命令:","serveHelp.step3":"浏览器会打开看板,更改会保存到真实文件。","serveHelp.copy":"复制","serveHelp.copied":"已复制","serveHelp.close":"关闭","status.read":"读取于 {time}","status.readRefresh":"读取于 {time}。刷新。","status.refreshed":"看板已更新","status.upToDate":"已是最新","status.refreshFailed":"无法刷新看板。请检查网络连接后重试。","picked.refreshTitle":"刷新此文件夹","picked.refreshText":"浏览器无法自行重新读取文件夹。要刷新,请再次选择 **{name}**。如果选择其他文件夹,将改为打开那个看板。","picked.refreshContinue":"选择文件夹","picked.refreshServe":"要自动更新,请使用 **kanbanmd serve**。","picked.refreshHow":"如何操作?","picked.edit":"用 kanbanmd 编辑","picked.editHint":"要在任何浏览器中编辑此看板,请在该文件夹中运行“kanbanmd serve”(需要 Node)。点击复制命令。","prompt.boardUrl":"看板地址(其 kanban.md 文件或文件夹,或 GitHub 链接):","welcome.openUrl":"或从网址打开看板","header.home":"Kanban.md 网站","header.source":"GitHub 上的源代码","header.newTask":"任务","header.archives":"归档","header.columns":"列设置","menu.language":"语言","menu.theme":"主题","menu.themeAuto":"自动","menu.themeLight":"浅色","menu.themeDark":"深色","filters.title":"筛选","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若希望保留历史记录, 请改用“归档”功能.',"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),s={Critical:"taskForm.priorityCritical",High:"taskForm.priorityHigh",Medium:"taskForm.priorityMedium",Low:"taskForm.priorityLow"};if(s[a]){return n+(n?" ":"")+t(s[a])}return e}function priorityConfigEntry(e){return(config.priorities||[]).find(t=>clean(t)===clean(e||""))}function priorityClass(e){const t=getFirstEmoji(priorityConfigEntry(e)||e||"");return priorityIconClasses[t]||"Default"}function priorityLabel(e){return displayPriority(clean(e||""))}const PRIO_SHAPES={up2:'<path d="M4 7.5l4-4 4 4M4 12.5l4-4 4 4"/>',up:'<path d="M4 10l4-4 4 4"/>',eq:'<path d="M4 6.5h8M4 10.5h8"/>',down:'<path d="M4 6l4 4 4-4"/>',down2:'<path d="M4 3.5l4 4 4-4M4 8.5l4 4 4-4"/>',none:'<circle cx="8" cy="8" r="2.5"/>'},PRIO_LADDERS={1:["eq"],2:["up","down"],3:["up","eq","down"],4:["up2","up","eq","down"],5:["up2","up","eq","down","down2"]};function prioShape(e){const t=(config.priorities||[]).map(clean),n=t.indexOf(clean(e||""));if(n<0)return"none";const a=t.length;return(PRIO_LADDERS[a]||PRIO_LADDERS[5])[PRIO_LADDERS[a]?n:Math.round(4*n/(a-1))]}function priorityIcon(e,t=""){return`<span class="prio ${priorityClass(e)}" ${t}><svg viewBox="0 0 16 16" aria-hidden="true">${PRIO_SHAPES[prioShape(e)]}</svg></span>`}function priorityHtml(e){return`<span class="prio-label">${priorityIcon(e,'aria-hidden="true"')}${escapeHtml(priorityLabel(e))}</span>`}function updateStaticTexts(){document.title=t("page.title"),document.getElementById("headerTitle").textContent=t("header.title"),document.getElementById("renameProjectBtn").title=t("header.renameProject"),document.querySelector("#renameProjectBtn .mi-label").textContent=t("header.renameProject"),document.getElementById("deleteProjectBtn").title=t("header.deleteProject"),document.querySelector("#deleteProjectBtn .mi-label").textContent=t("header.deleteProject"),document.querySelector("#openUrlBtn .mi-label").textContent=t("header.openUrl"),document.querySelector("#homeLink .mi-label").textContent=t("header.home"),document.querySelector("#sourceLink .mi-label").textContent=t("header.source"),document.getElementById("newTaskBtn").textContent=t("header.newTask"),document.querySelector("#archiveBtn .mi-label").textContent=t("header.archives"),document.querySelector("#manageColsBtn .mi-label").textContent=t("header.columns"),document.getElementById("languageLabel").textContent=t("menu.language"),document.getElementById("filterToggleLabel").textContent=t("filters.title"),"function"==typeof updateThemeUi&&updateThemeUi(),ddRoot("languageSelector")._ddOptions||ddSetOptions("languageSelector",[{value:"en",label:"English"},{value:"fr",label:"Français"},{value:"de",label:"Deutsch"},{value:"zh_cn",label:"简体中文"}]),ddSetValue("languageSelector",currentLanguage,!0),renderWelcomeScreen(),applySourceUi(),renderTaskDetailModal(),updateTaskFormLabels(),renderColumnsModal(),renderArchivesModal(),updateFilterLabels()}function renderWelcomeScreen(){const e=document.getElementById("welcomeScreen");e&&(e.innerHTML=`\n ${webNoticeHtml()}\n <h2>${t("welcome.title")}</h2>\n <p>${t("welcome.description")}</p>\n <button onclick="document.getElementById('selectFolderBtn').click()" class="btn btn-primary" style="height: 40px; padding: 0 var(--s-5);">\n ${t("welcome.start")}\n </button>\n <p class="welcome-alt"><button type="button" class="btn btn-ghost" onclick="openBoardFromUrl()">${t("welcome.openUrl")}</button></p>\n <div class="welcome-card">\n <h3>${t("welcome.howItWorks")}</h3>\n <ol>\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 class="note${"showDirectoryPicker"in window?"":" warn"}">${t("welcome.browserWarning")}</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&&readOnly()?e.innerHTML="":e&&(e.innerHTML=`\n <div class="push">\n <button class="btn btn-danger" onclick="deleteCurrentTask()">${t("taskDetail.delete")}</button>\n <button class="btn btn-secondary" onclick="archiveCurrentTask()">${t("taskDetail.archive")}</button>\n </div>\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.querySelector('label[for="taskTags"] + input + small');n&&(n.textContent=t("taskForm.tagsHelp"));const a=document.querySelector("#taskNotes + small");a&&(a.textContent=t("taskForm.notesHelp"));const s=document.querySelector("#formSubtasksList").previousElementSibling;s&&(s.textContent=t("taskForm.subtasksLabel"));const r=document.querySelector("#formSubtaskInput + button");r&&(r.textContent=t("taskForm.subtaskAdd"));const o=document.querySelector('#newTaskForm .actions button[type="button"]');o&&(o.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={tag:t("filters.tags"),category:t("filters.category"),user:t("filters.user"),priority:t("filters.priority")};e.querySelectorAll(".fdrop").forEach(e=>{e.querySelector(".flabel").textContent=a[e.dataset.type].replace(/\s*[:\uFF1A]\s*$/,"")}),renderFilters()}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"));const a=document.getElementById("backlogSearch");a&&(a.placeholder=t("backlog.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);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"),s=a.objectStore("settings"),r=s.get(PROJECTS_KEY),o=await new Promise((e,t)=>{r.onsuccess=()=>e(r.result||[]),r.onerror=()=>t(r.error)}),i=e.name,l=o.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?o[l].customName:""),lastAccessed:Date.now()};l>=0?(d.customName=null!==n?n:o[l].customName,o[l]=d):o.unshift(d);const u=o.slice(0,10);s.put(u,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(){"function"==typeof updateOrderingMenu&&updateOrderingMenu(),"function"==typeof updateHousekeepingBar&&updateHousekeepingBar();const e=await loadRecentProjects(),n=document.getElementById("projectDd"),a=document.getElementById("renameProjectBtn"),s=document.getElementById("deleteProjectBtn");if(e.length>0){n.style.display="block";const r="none"!==document.getElementById("kanbanView").style.display,o=r&&folderHandle()?folderHandle().name:"";r&&folderHandle()?(a.style.display="inline-flex",s.style.display="inline-flex"):(a.style.display="none",s.style.display="none");const i=[];o||i.push({value:"",label:t("projects.select")});let l="";e.forEach((e,t)=>{const n=new Date(e.lastAccessed).toLocaleDateString("fr-FR"),a=e.customName||e.name,s=e.customName?` (📁 ${e.name})`:"";r&&e.name===o&&(l=String(t)),i.push({value:String(t),label:`${a}${s} - ${n}`})}),ddSetOptions("projectSelector",i),ddSetValue("projectSelector",l,!0)}else n.style.display="none",a.style.display="none",s.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)?(useSource(folderSource(a.handle)),await saveDirectoryHandle(a.handle),await loadKanbanFile(),showBoardView(!0),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(){const e=folderHandle();if(e)try{const n=(await loadRecentProjects()).find(t=>t.name===e.name);if(n){const a=n.customName||n.name,s=prompt(t("prompt.renameProject"),a);null!==s&&s.trim()!==a&&(await saveDirectoryHandle(e,s.trim()),showNotification(t("notif.projectRenamed"),"success"))}}catch(e){console.error("Error renaming project:",e),showNotification(t("notif.renameError"),"error")}}async function deleteCurrentProject(){const e=folderHandle();if(e)try{const n=await loadRecentProjects(),a=n.find(t=>t.name===e.name);if(a){const s=a.customName||a.name;if(confirm(t("confirm.deleteProject",{name:s}))){const a=n.filter(t=>t.name!==e.name),s=(await openDB()).transaction("settings","readwrite");s.objectStore("settings").put(a,PROJECTS_KEY),await new Promise((e,t)=>{s.oncomplete=e,s.onerror=()=>t(s.error)}),useSource(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..."),useSource(folderSource(e)),await loadKanbanFile(),showBoardView(!0),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}function fingerprint(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return(t>>>0).toString(16)+":"+e.length}function folderSource(e){const t=async t=>{try{return await(await e.getFileHandle(t)).getFile()}catch(e){if("NotFoundError"===e.name)return null;throw e}};return{kind:"folder",key:e.name,label:e.name,writable:!0,handle:e,async read(e){const n=await t(e);return n?{text:await n.text(),version:n.lastModified}:null},async version(e){const n=await t(e);return n?n.lastModified:null},async write(t,n){const a=await(await e.getFileHandle(t,{create:!0})).createWritable();await a.write(n),await a.close()}}}window.addEventListener("DOMContentLoaded",async()=>{const e=boardParam();renderStatusBar(),null!==e?await openBoardParam(e):window.KANBANMD_SERVICE?await openServiceBoard(window.KANBANMD_SERVICE):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()=>{if("showDirectoryPicker"in window)try{const e={};folderHandle()&&(e.startIn=folderHandle());const n=await window.showDirectoryPicker(e);useSource(folderSource(n)),await saveDirectoryHandle(n),await loadKanbanFile(),showBoardView(!0),await updateProjectSelector(),showNotification(t("notif.folderLoaded"),"success")}catch(e){"AbortError"!==e.name&&(showNotification(t("notif.folderError"),"error"),console.error(e))}else pickFolderFiles()});const WEB_MAX_BYTES=5242880;function webSource(e){const t={},n=n=>t[n]||(t[n]=fetchBoardFile(e,n));return{kind:"web",key:e.href,label:e.label,href:e.href,place:e,writable:!1,async read(e){const t=await n(e);return null===t?null:{text:t,version:fingerprint(t)}},async version(e){const t=await n(e);return null===t?null:fingerprint(t)},async write(){throw new Error("This board is read-only")}}}function serviceSource(e){const t={kind:"service",key:e.path||e.name,label:e.name,path:e.path||e.name,writable:!0,async call(t,n){let a;try{a=await fetch(`${e.api}/file/${encodeURIComponent(t)}`,{cache:"no-store",credentials:"same-origin",...n,headers:{"X-Kanbanmd":"1",...n&&n.headers||{}}})}catch(e){throw setServiceState("stopped"),e}return setServiceState(serviceStateOf(a.status)),a},async read(e){const n=await t.call(e);if(404===n.status)return null;if(!n.ok)throw new Error(`kanbanmd answered ${n.status}`);return{text:await n.text(),version:n.headers.get("ETag")}},async version(e){const n=await t.call(e,{method:"HEAD"});if(404===n.status)return null;if(!n.ok)throw new Error(`kanbanmd answered ${n.status}`);return n.headers.get("ETag")},async write(e,n){const a=await t.call(e,{method:"PUT",headers:{"Content-Type":"text/markdown; charset=utf-8"},body:n});if(!a.ok)throw new Error(`kanbanmd answered ${a.status}`)}};return t}function pickedSource(e){const t={};let n="";for(const a of e){const e=(a.webkitRelativePath||"").split("/");!n&&e.length>1&&(n=e[0]),2===e.length&&/\.md$/i.test(e[1])&&(t[e[1]]=a)}return{kind:"picked",key:n,label:n,writable:!1,files:t,async read(e){const n=t[e];if(!n)return null;if(n.size>5242880)throw webError("tooLarge");const a=await n.text();return{text:a,version:fingerprint(a)}},async version(e){const n=t[e];return n?n.size>5242880?"large:"+n.size:fingerprint(await n.text()):null},async write(){throw new Error("This board is read-only")}}}function webError(e,t){const n=new Error(e);return n.webCode=e,n.status=t,n}async function fetchBoardFile(e,t){const n="kanban.md"===t,a=n?e.href:boardFileUrl(e.href,t);if(!a)return null;try{let e;try{e=await fetch(a,{credentials:"omit",referrerPolicy:"no-referrer",cache:"no-cache"})}catch(e){throw webError("network")}if(404===e.status||410===e.status)return null;if(!e.ok)throw webError("http",e.status);if(Number(e.headers.get("content-length"))>5242880)throw webError("tooLarge");const t=await e.text();if(t.length>5242880)throw webError("tooLarge");if(/text\/html/i.test(e.headers.get("content-type")||"")&&!looksLikeBoard(t))throw webError("notBoard");return t}catch(e){if(n)throw e;return console.warn(`Skipped ${t} from the web:`,e.message),null}}let sourceOpenedAt=0;function useSource(e){persistBase=null,source=e,sourceOpenedAt=Date.now(),applySourceUi()}function readOnly(){return!(!source||source.writable)}function folderHandle(){return source&&"folder"===source.kind?source.handle:null}function boardLoaded(){return!(!source||!persistBase)}let webNotice=null;function boardParam(){return new URLSearchParams(location.search).get("board")}function stripBoardParam(){const e=new URL(location.href);e.searchParams.delete("board"),history.replaceState(null,"",e.href)}async function openBoardParam(e){const t=resolveBoardUrl(e,location.href);t?t.sameOrigin?await openWebBoard(t):showWebNotice({kind:"confirm",place:t}):showWebNotice({kind:"error",code:"invalid",place:null}),await updateProjectSelector()}function openBoardFromUrl(){const e=source&&"web"===source.kind?source.href:"",n=prompt(t("prompt.boardUrl"),e);if(null===n||!n.trim())return;const a=resolveBoardUrl(n,location.href);if(!a)return void alert(t("web.error.invalid"));const s=new URL(location.href);s.search="",s.searchParams.set("board",a.href),location.assign(s.href)}async function openWebBoard(e){const t=webSource(e);try{const e=await t.read("kanban.md");if(!e)throw webError("notFound");if(!looksLikeBoard(e.text))throw webError("notBoard")}catch(t){return console.warn("Could not open the board:",t),showWebNotice({kind:"error",code:t.webCode||"network",status:t.status,place:e}),!1}return webNotice=null,useSource(t),await loadKanbanFile(),showBoardView(!1),!0}function showBoardView(e){document.getElementById("welcomeScreen").style.display="none",document.getElementById("kanbanView").style.display="block",document.getElementById("filterBar").style.display="block",document.getElementById("archiveBtn").style.display="inline-flex",e&&(document.getElementById("newTaskBtn").style.display="inline-flex",document.getElementById("manageColsBtn").style.display="inline-flex")}function confirmWebBoard(){if(!webNotice||"confirm"!==webNotice.kind)return;const{place:e}=webNotice;return webNotice=null,openWebBoard(e)}async function cancelWebBoard(){webNotice=null,stripBoardParam(),renderWelcomeScreen(),await tryRestorePreviousDirectory()}function showWebNotice(e){webNotice=e,renderWelcomeScreen()}function webNoticeHtml(){if(!webNotice)return"";const{kind:e,place:n,code:a,status:s}=webNotice,r=n?`<p class="web-notice-url"><a href="${escapeHtml(n.href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(n.label)}</a></p>`:"";return"confirm"===e?`<div class="welcome-card web-notice">\n <h3>${escapeHtml(t("web.confirmTitle"))}</h3>\n <p>${escapeHtml(t("web.confirmText",{host:new URL(n.href).host}))}</p>\n ${r}\n <div class="web-notice-actions">\n <button class="btn btn-secondary" onclick="cancelWebBoard()">${escapeHtml(t("web.cancel"))}</button>\n <button class="btn btn-primary" onclick="confirmWebBoard()">${escapeHtml(t("web.confirmOpen"))}</button>\n </div>\n </div>`:`<div class="welcome-card web-notice web-notice--error" role="alert">\n <h3>${escapeHtml(t("web.errorTitle"))}</h3>\n <p>${escapeHtml(t("web.error."+a,{status:s||""}))}</p>\n ${r}\n </div>`}function updateSourceMenu(){const e=window.KANBANMD_SERVICE,n="showDirectoryPicker"in window,a=document.querySelector("#selectFolderBtn .mi-label");a&&(a.textContent=t(n?e?"header.folderAnother":"header.folder":"header.folderReadOnly")),document.getElementById("pickedEditBtn").style.display=source&&"picked"===source.kind?"":"none",document.querySelector("#pickedEditBtn .mi-label").textContent=t("picked.edit"),document.getElementById("pickedEditBtn").title=t("picked.editHint"),applyPickDialogTexts();const s=document.getElementById("serviceBackBtn");s&&(s.style.display=!e||source&&"service"===source.kind?"none":"",s.querySelector(".mi-label").textContent=t("header.backToService"),s.title=e&&e.path||"")}function applySourceUi(){updateSourceMenu(),document.body.classList.toggle("readonly",readOnly()),renderStatusBar(),source&&"web"!==source.kind&&null!==boardParam()&&stripBoardParam()}let pickNoteMode="first";function showPickNote(e){pickNoteMode=e,applyPickDialogTexts(),document.getElementById("pickNoteModal").classList.add("active"),document.getElementById("pickNoteContinue").focus()}function pickFolderFiles(){showPickNote("first")}function refreshPickedFolder(){showPickNote("refresh")}function closePickNote(){document.getElementById("pickNoteModal").classList.remove("active")}function continuePickNote(){closePickNote(),document.getElementById("pickFolderInput").click()}function openServeHelp(){document.getElementById("serveHelpCopy").textContent=t("serveHelp.copy"),document.getElementById("serveHelpModal").classList.add("active"),document.getElementById("serveHelpClose").focus()}function closeServeHelp(){document.getElementById("serveHelpModal").classList.remove("active")}async function copyServeCommand(){const e=document.getElementById("serveHelpCmd").textContent,n=document.getElementById("serveHelpCopy");try{await navigator.clipboard.writeText(e)}catch(e){return}n.textContent=t("serveHelp.copied"),setTimeout(()=>{n.textContent=t("serveHelp.copy")},1500)}function applyPickDialogTexts(){const e=(e,n)=>{document.getElementById(e).textContent=t(n)},n="refresh"===pickNoteMode&&source;e("pickNoteTitle",n?"picked.refreshTitle":"picked.noteTitle"),e("pickNoteContinue",n?"picked.refreshContinue":"picked.noteContinue"),e("pickNoteCancel","web.cancel"),document.getElementById("pickNoteText").innerHTML=markdownToHtml(n?t("picked.refreshText",{name:source.label}):t("picked.noteText")),document.getElementById("pickNoteServe").style.display=n?"":"none",document.getElementById("pickNoteServeText").innerHTML=markdownToHtml(t("picked.refreshServe")).replace(/^<p[^>]*>|<\/p>$/g,""),e("pickNoteServeLink","picked.refreshHow"),e("serveHelpTitle","serveHelp.title"),e("serveHelpIntro","serveHelp.intro"),e("serveHelpStep1","serveHelp.step1"),e("serveHelpStep2","serveHelp.step2"),e("serveHelpStep3","serveHelp.step3"),e("serveHelpClose","serveHelp.close"),e("serveHelpCopy","serveHelp.copy")}function pickError(e){source?showNotification(t("web.error."+e,{status:""}),"error"):showWebNotice({kind:"error",code:e,place:null})}async function openPickedFolder(e){const n=pickedSource(e),a=source;try{if(!n.label)throw webError("noBoard");const e=await n.read("kanban.md");if(!e)throw webError("noBoard");if(!looksLikeBoard(e.text))throw webError("notBoard")}catch(e){return e.webCode||console.warn("Could not open the folder:",e),pickError(e.webCode||"network"),!1}if(webNotice=null,a&&"picked"===a.kind&&a.label===n.label){try{const e=await adoptRefreshed(n,a);null!==e&&showNotification(t(e?"status.refreshed":"status.upToDate"),"success")}catch(e){showNotification(t("status.refreshFailed"),"error")}return!0}return useSource(n),await loadKanbanFile(),showBoardView(!1),await updateProjectSelector(),showNotification(t("picked.opened",{name:n.label}),"success"),!0}async function openServiceBoard(e){useSource(serviceSource(e)),await loadKanbanFile(),showBoardView(!0),await updateProjectSelector()}async function backToServiceBoard(){if(!window.KANBANMD_SERVICE)return;const e=await probeService();if("ok"===e)return openServiceBoard(window.KANBANMD_SERVICE);showNotification(t("expired"===e?"status.expired":"status.stopped"),"error")}document.getElementById("pickFolderInput").addEventListener("change",async e=>{const t=Array.from(e.target.files||[]);e.target.value="",t.length?await openPickedFolder(t):pickError("noBoard")});let serviceState="ok",lastSyncAt=0;function serviceStateOf(e){return 401===e||403===e?"expired":"ok"}function setServiceState(e){e!==serviceState&&(serviceState=e,renderStatusBar())}function markSynced(){lastSyncAt=Date.now();const e=document.getElementById("statusSync");e&&source&&source.writable&&(e.textContent=t("status.synced",{time:new Date(lastSyncAt).toLocaleTimeString()}))}async function probeService(){const e=window.KANBANMD_SERVICE;let t;try{t=serviceStateOf((await fetch(`${e.api}/file/kanban.md`,{method:"HEAD",cache:"no-store",credentials:"same-origin",headers:{"X-Kanbanmd":"1"}})).status)}catch(e){t="stopped"}return setServiceState(t),t}async function pingService(){!window.KANBANMD_SERVICE||"visible"!==document.visibilityState||source&&"service"===source.kind||await probeService()}setInterval(pingService,3e3);const REFRESH_ICON='<svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M13 8a5 5 0 1 1-1.5-3.5M13 3v3h-3"/></svg>',LOCK_ICON='<svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><rect x="3.5" y="7" width="9" height="6.5" rx="1"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/></svg>';function renderStatusBar(){const e=document.getElementById("statusBar");if(!e)return;const n=!!source||!!window.KANBANMD_SERVICE;if(document.body.classList.toggle("has-status",n),e.style.display=n?"":"none",e.textContent="",!n)return;const a=(e,t,n)=>{const a=document.createElement(e);return t&&(a.className=t),void 0!==n&&(a.textContent=n),a},s=e=>{const t=a("span",`status-dot status-dot--${e}`);return t.setAttribute("aria-hidden","true"),t},r=window.KANBANMD_SERVICE,o={ok:"",stopped:t("status.stopped"),expired:t("status.expired")},i=a("div","status-left");if(i.setAttribute("role","status"),source)if("service"===source.kind){const e=a("span","status-seg");e.title=source.path,"ok"===serviceState?e.append(s("ok"),a("strong","",t("status.served")),a("span","status-label",source.label)):e.append(s(serviceState),a("strong","",o[serviceState])),i.append(e)}else if(source.writable){if(source.label){const e=a("span","status-seg");e.append(a("span","status-label",source.label)),i.append(e)}}else{const e=a("span","status-seg"),n=a("strong","status-badge");let s;n.innerHTML=LOCK_ICON,n.append(t("web.readOnly")),source.href?(s=a("a","status-label",source.label),s.href=source.href,s.target="_blank",s.rel="noopener noreferrer"):s=a("span","status-label",source.label),e.append(n,s),i.append(e)}else;if(r&&(!source||"service"!==source.kind)){const e=a("button","btn btn-ghost btn-sm status-chip");e.type="button",e.title=r.path||"",e.append(s(serviceState),a("span","","ok"===serviceState?t("header.backToService"):o[serviceState])),e.addEventListener("click",backToServiceBoard),i.append(e)}let l;if(source&&!source.writable&&sourceOpenedAt){const e=new Date(sourceOpenedAt).toLocaleTimeString();l=a("button","status-sync status-refresh"+(refreshing?" is-busy":"")),l.type="button",l.disabled=refreshing,l.title=new Date(sourceOpenedAt).toLocaleString(),l.setAttribute("aria-label",t("status.readRefresh",{time:e}));const n=a("span","status-refresh-icon");n.innerHTML=REFRESH_ICON,l.append(a("span","",t("status.read",{time:e})),n),l.addEventListener("click",refreshBoard)}else l=a("span","status-sync"),source&&source.writable&&lastSyncAt&&(l.textContent=t("status.synced",{time:new Date(lastSyncAt).toLocaleTimeString()}));l.id="statusSync",e.append(i,l)}let refreshing=!1;function setRefreshing(e){refreshing=e,renderStatusBar()}async function adoptRefreshed(e,t){const n=await e.read("kanban.md");if(!n)throw webError("notFound");if(!looksLikeBoard(n.text))throw webError("notBoard");const a={board:n.version,archive:await e.version("archive.md"),backlog:await e.version(backlogFileNameOf(n.text))};let s=null;try{s=await readVersions()}catch(e){}if(source!==t)return null;if(s&&deepEqual(s,a))return sourceOpenedAt=Date.now(),renderStatusBar(),!1;const r=document.getElementById("kanbanView"),o=r.scrollTop,i=r.scrollLeft;return useSource(e),await loadKanbanFile(),showBoardView(!1),r.scrollTop=o,r.scrollLeft=i,!0}async function refreshBoard(){if(!source||source.writable||refreshing)return;if("picked"===source.kind)return void refreshPickedFolder();const e=source;setRefreshing(!0);try{const n=await adoptRefreshed(webSource(e.place),e);null!==n&&showNotification(t(n?"status.refreshed":"status.upToDate"),"success")}catch(e){console.warn("Could not refresh the board:",e),showNotification(t("status.refreshFailed"),"error")}finally{setRefreshing(!1)}}async function loadKanbanFile(){persistBase=null;try{const e=await source.read("kanban.md");if(!e&&source.writable)return await createDefaultFiles();if(!e)throw new Error("kanban.md not found");currentKanbanContent=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;if(source.writable)if(loadedFormatVersion<3&&tasks.length===t&&n===t){const e=loadedFormatVersion;await backupOriginal("kanban.md",currentKanbanContent,e)&&await writeWholeBoard({addSourceComment:!0})?console.log(`Migrated kanban.md from v${e} to v3 (one metadata field per line)`):console.warn("Skipped migration: backup or save failed. File left unchanged.")}else loadedFormatVersion<3&&console.warn(`Skipped migration: incomplete parse (${tasks.length}/${t} task blocks). File left unchanged.`);else;await snapshotFromDisk()}catch(e){showNotification(t("notif.loadError")||"Erreur lors du chargement du fichier","error"),console.error("Error loading kanban.md:",e)}}async function createDefaultFiles(){showNotification(t("notif.initializingFolder"),"success"),console.log("kanban.md not found, creating default files...");try{currentKanbanContent=createDefaultBoardContent(),await source.write("kanban.md",currentKanbanContent),await source.write("archive.md",createDefaultArchiveContent(archiveStrings())),showNotification(t("notif.filesInitialized"),"success"),parseMarkdown(currentKanbanContent),updateAutocomplete(),renderKanban(),await snapshotFromDisk()}catch(e){showNotification(t("notif.filesError"),"error"),console.error(e)}}async function backupOriginal(e,t,n=1){try{const a=e.replace(/\.md$/,`.v${n}-backup.md`);return null!==await source.version(a)||(await source.write(a,t),console.log("Backup written:",a)),!0}catch(e){return console.warn("Backup failed — migration aborted to keep the original intact:",e),!1}}function parseMarkdown(e){const t=parseBoard(e);config=t.config,tasks=t.tasks,loadedFormatVersion=t.formatVersion,console.log(`Parsed ${tasks.length} tasks:`,tasks.map(e=>`${e.id} (${e.status})`).join(", "))}let persistBase=null,currentArchiveText=null,currentBacklogText=null,backlogFileName="backlog.md",persistChain=Promise.resolve(!0),persistBusy=!1,pollBusy=!1;const lastSeenVersion={board:null,archive:null,backlog:null},jsonClone=e=>JSON.parse(JSON.stringify(e)),PERSIST_LISTS=["categories","users","priorities","tags"];function currentPageState(){return{config:config,tasks:tasks,archive:archivedTasks}}function stateEquals(e,t){return deepEqual(e.tasks,t.tasks)&&deepEqual(e.archive,t.archive)&&deepEqual(e.config.columns,t.config.columns)&&deepEqual(e.config.backlogFile,t.config.backlogFile)&&deepEqual(e.config.housekeeping,t.config.housekeeping)&&PERSIST_LISTS.every(n=>deepEqual(e.config[n],t.config[n]))}async function readProjectFiles(){const e=await source.read("kanban.md");if(!e){const e=new Error("kanban.md is missing");throw e.name="NotFoundError",e}const t=await source.read("archive.md");backlogFileName=backlogFileNameOf(e.text);const n=source.writable||/\*\*Backlog file\*\*:/.test(e.text)?await source.read(backlogFileName):null;return lastSeenVersion.board=e.version,lastSeenVersion.archive=t?t.version:null,lastSeenVersion.backlog=n?n.version:null,{board:e.text,archive:t?t.text:null,backlog:n?n.text:null}}async function writeProjectFile(e,t){await source.write(e,t)}async function readVersions(){return{board:await source.version("kanban.md"),archive:await source.version("archive.md"),backlog:await source.version(backlogFileName)}}async function refreshVersions(){try{Object.assign(lastSeenVersion,await readVersions())}catch(e){}}function adoptTexts(e,t,n,{expect:a=null,force:s=!1}={}){const r=parseBoard(e),o=null===t?{tasks:[],formatVersion:3}:parseArchive(t,r.config.columns),i=resolveBacklogFile(r.config),l=i&&null!==n?parseBacklog(n,r.config.columns,i.column):{tasks:[]},c={config:r.config,tasks:[...r.tasks,...l.tasks],archive:o.tasks};loadedFormatVersion=r.formatVersion,loadedArchiveVersion=o.formatVersion,currentKanbanContent=e,currentArchiveText=t,currentBacklogText=n,persistBase={config:jsonClone(r.config),tasks:jsonClone(c.tasks),archive:jsonClone(o.tasks),boardText:e,archiveText:t,backlogText:n};return!(a&&!stateEquals(currentPageState(),a)||!s&&stateEquals(currentPageState(),c))&&(config=r.config,tasks=c.tasks,archivedTasks=o.tasks,!0)}async function snapshotFromDisk(){const e=await readProjectFiles();adoptTexts(e.board,e.archive,e.backlog),refreshUi()}function refreshUi(){updateAutocomplete();const e=document.getElementById("kanbanView"),t=e.scrollTop;renderKanban(),e.scrollTop=t,updateOrderingMenu(),document.getElementById("archiveModal").classList.contains("active")&&renderArchiveList(document.getElementById("archiveSearch").value),document.getElementById("backlogModal").classList.contains("active")&&renderBacklogList(document.getElementById("backlogSearch").value)}function autoSave(){return persistChain=persistChain.then(persistNow,persistNow),persistChain}async function persistNow(){if(!source||!source.writable||!persistBase)return!1;persistBusy=!0;try{const{ops:e,unsupported:t}=diffToOps(persistBase,currentPageState());return!e.length&&!t.length||(await commit(e,{unsupported:t})).ok}catch(e){return console.error("Save failed:",e),showNotification(t("notif.saveFailed"),"error"),!1}finally{persistBusy=!1}}async function commit(e,{unsupported:n=[],explicit:a=!1}={}){if(!source||!source.writable)return{ok:!1,changed:!1};for(let s=0;s<4;s++){const s=jsonClone(currentPageState()),r=await readProjectFiles(),o=r.board!==persistBase.boardText||r.archive!==persistBase.archiveText||r.backlog!==persistBase.backlogText;let i=null,l=null;if(!n.length)try{i=applyOps({board:r.board,archive:r.archive,backlog:r.backlog},e)}catch(e){l=e,console.warn("Ops not applicable:",e&&e.code,e&&e.message)}if(i){const e=await readProjectFiles();if(e.board!==r.board||e.archive!==r.archive||e.backlog!==r.backlog)continue;for(const e of i.files)await writeProjectFile(e.name,e.text);const t=adoptTexts(i.board,i.archive,i.backlog,{expect:s});return await refreshVersions(),t&&refreshUi(),{ok:!0,changed:i.files.length>0}}return a?(showNotification(t("notif.saveFailed"),"error"),await reloadFromDisk({quiet:!0}),{ok:!1,changed:!1}):o?(await reloadFromDisk({quiet:!0}),showNotification(t("notif.mergeFailed",{reason:l?l.message:n[0]||""}),"error"),{ok:!1,changed:!1}):{ok:await writeWholeFiles(),changed:!0}}return showNotification(t("notif.saveFailed"),"error"),{ok:!1,changed:!1}}async function writeWholeBoard(e){const t=generateMarkdown(e);return await writeProjectFile("kanban.md",t),currentKanbanContent=t,t}async function writeWholeArchive(){const e=generateArchive(archivedTasks,archiveStrings(),{sourceComment:null===currentArchiveText||hasSourceComment(currentArchiveText)});return await writeProjectFile("archive.md",e),e}async function writeWholeBacklog(){const e=resolveBacklogFile(config);if(!e)return currentBacklogText;const t=generateArchive(tasks.filter(t=>t.status===e.column),DEFAULT_BACKLOG_STRINGS,{sourceComment:null===currentBacklogText||hasSourceComment(currentBacklogText)});return await writeProjectFile(e.file,t),t}async function writeWholeFiles(){const e=jsonClone(currentPageState()),t=archivedTasks.length||null!==currentArchiveText?await writeWholeArchive():currentArchiveText,n=await writeWholeBacklog();return adoptTexts(await writeWholeBoard(),t,n,{expect:e}),await refreshVersions(),!0}async function reloadFromDisk({quiet:e=!1}={}){await persistChain;const n=jsonClone(currentPageState()),a=await readProjectFiles();if(!stateEquals(currentPageState(),n))return!1;const s=tasks.slice();adoptTexts(a.board,a.archive,a.backlog,{expect:n,force:!0}),refreshUi();const r=document.getElementById("newTaskModal");if(isEditMode&&r.classList.contains("active")){const e=document.getElementById("taskEditId").value,n=s.find(t=>t.id===e),a=tasks.find(t=>t.id===e);a?n&&!deepEqual(n,a)&&confirm(t("confirm.taskChangedElsewhere",{id:e}))&&openTaskModal(a):(showNotification(t("notif.taskRemovedElsewhere",{id:e}),"error"),closeTaskModal())}if(document.getElementById("taskModal").classList.contains("active")&&currentDetailTask){const e=tasks.find(e=>e.id===currentDetailTask.id),t=e||archivedTasks.find(e=>e.id===currentDetailTask.id);t?showTaskDetail(t,{...detailCtx,archived:!e}):closeModal()}return e||showNotification(t("notif.reloaded"),"success"),!0}async function pollOnce(){if("visible"===document.visibilityState&&source&&source.writable&&persistBase&&!persistBusy&&!pollBusy){pollBusy=!0;try{const e=await readVersions();if(markSynced(),e.board===lastSeenVersion.board&&e.archive===lastSeenVersion.archive&&e.backlog===lastSeenVersion.backlog)return;const t=await readProjectFiles();if(t.board===persistBase.boardText&&t.archive===persistBase.archiveText&&t.backlog===persistBase.backlogText)return;await reloadFromDisk()}catch(e){console.warn("Change check skipped:",e&&e.message)}finally{pollBusy=!1}}}function escapeHtml(e){return String(e??"").replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[e]))}function markdownToHtml(e){if(!e)return"";let t=e;const n=[];t=t.replace(/```([^\n`]*)\n?([\s\S]*?)```/g,(e,t,a)=>{const s=escapeHtml((t||"").trim()||"text"),r=`\n__CODE_BLOCK_${n.length}__\n`,o=`<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;">${s}</div><pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>${a.replace(/\n$/,"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</code></pre></div>`;return n.push(o),r}),t=t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");const a=[];t=t.replace(/`([^`]+)`/g,(e,t)=>(a.push(`<code style="background: #2d2d2d; color: #f8f8f2; padding: 0.125rem 0.35rem; border-radius: 3px; font-family: 'Consolas', 'Monaco', monospace; font-size: 0.9em;">${t}</code>`),`__INLINE_CODE_${a.length-1}__`)),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,(e,t,n)=>{const a=n.trim();return`<a href="${(/^(https?:|mailto:|#|\/)/i.test(a)?a:"#").replace(/"/g,"&quot;")}" target="_blank" rel="noopener noreferrer" style="color: var(--primary); text-decoration: underline;">${t}</a>`});const s=t.split("\n");let r=!1,o=[];for(let e of s)""===e.trim()||e.trim().match(/^__CODE_BLOCK_\d+__$/)?(r&&(o.push("</ul>"),r=!1),e.trim().startsWith("__CODE_BLOCK_")?o.push(e.trim()):""===e.trim()&&(r||o.push(""))):e.trim().startsWith("- ")?(r||(o.push('<ul style="margin: 0.5rem 0; padding-left: 1.5rem;">'),r=!0),o.push("<li>"+e.trim().substring(2)+"</li>")):(r&&(o.push("</ul>"),r=!1),e.trim()&&o.push('<p style="margin: 0.5rem 0;">'+e+"</p>"));return r&&o.push("</ul>"),t=autolinkTaskIds(o.join("\n")),n.forEach((e,n)=>{t=t.replace(`__CODE_BLOCK_${n}__`,()=>e)}),a.forEach((e,n)=>{t=t.replace(`__INLINE_CODE_${n}__`,()=>e)}),t}function ddRoot(e){return document.getElementById(e).closest(".dd")}function ddOptionEls(e){return[...e.querySelectorAll('.dd-menu .dd-opt, .dd-menu input[type="checkbox"]')]}function ddOpen(e){ddCloseAll(e),e.classList.add("open"),e.querySelector(".dd-trigger").setAttribute("aria-expanded","true"),e.dispatchEvent(new CustomEvent("ddopen",{bubbles:!0}))}function ddClose(e){e.classList.remove("open"),e.querySelector(".dd-trigger").setAttribute("aria-expanded","false")}function ddCloseAll(e){document.querySelectorAll(".dd.open").forEach(t=>{t!==e&&ddClose(t)})}function ddFocusStart(e){const t=ddOptionEls(e);(t.find(e=>"true"===e.getAttribute("aria-selected"))||t[0])?.focus()}function ddEscape(e){return String(e??"").replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[e]))}function ddSetOptions(e,t){const n=ddRoot(e);n._ddOptions=t,n.querySelector(".dd-menu").innerHTML=t.map(e=>`<div class="dd-opt" role="option" tabindex="-1" data-value="${ddEscape(e.value)}">${e.html||`<span>${ddEscape(e.label)}</span>`}</div>`).join(""),ddSetValue(e,document.getElementById(e).value,!0)}function ddSetValue(e,t,n){const a=document.getElementById(e),s=ddRoot(e),r=s._ddOptions||[],o=r.find(e=>e.value===t)||r[0],i=o?o.value:"",l=a.value!==i;a.value=i,s.querySelector(".dd-trigger").innerHTML=o?o.html||`<span>${ddEscape(o.label)}</span>`:"",s.querySelectorAll(".dd-menu .dd-opt").forEach(e=>e.setAttribute("aria-selected",String(e.dataset.value===i))),l&&!n&&a.dispatchEvent(new Event("change",{bubbles:!0}))}function ddPick(e){const t=e.closest(".dd");ddSetValue(t.querySelector('input[type="hidden"]').id,e.dataset.value),ddClose(t),t.querySelector(".dd-trigger").focus()}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:t,tags:n}=extractUniqueValues();document.getElementById("categoriesList").innerHTML=e.map(e=>`<option value="${escapeHtml(e)}">`).join(""),document.getElementById("usersList").innerHTML=t.map(e=>`<option value="${escapeHtml(e)}">`).join(""),document.getElementById("tagsList").innerHTML=n.map(e=>`<option value="${escapeHtml(e)}">`).join(""),buildFilterMenus()}function filterOptions(e){const{categories:t,users:n,tags:a}=extractUniqueValues();return"tag"===e?a.map(e=>({value:e,label:escapeHtml(e)})):"category"===e?t.map(e=>({value:e,label:escapeHtml(e)})):"user"===e?n.map(e=>({value:normalizeUserId(e),label:escapeHtml(e)})):(config.priorities||[]).map(e=>({value:clean(e),label:priorityHtml(e)}))}function buildFilterMenus(){["tag","category","user","priority"].forEach(e=>{const n=document.getElementById("fmenu-"+e);if(!n)return;const a=filterOptions(e);n.innerHTML=0===a.length?`<div class="fempty">${t("empty.noTasks")}</div>`:a.map(t=>`<label class="fopt"><input type="checkbox" data-type="${e}" data-value="${escapeHtml(t.value)}" onchange="setFilterValue(this.dataset.type, this.dataset.value, this.checked)"><span>${t.label}</span></label>`).join("")}),syncFilterUi()}function setFilterValue(e,t,n){const a=activeFilters.findIndex(n=>n.type===e&&n.value===t);n&&a<0&&activeFilters.push({type:e,value:t}),!n&&a>=0&&activeFilters.splice(a,1),renderFilters(),renderKanban()}function syncFilterUi(){document.querySelectorAll('.fmenu input[type="checkbox"]').forEach(e=>{e.checked=activeFilters.some(t=>t.type===e.dataset.type&&t.value===e.dataset.value)}),document.querySelectorAll(".fdrop").forEach(e=>{const t=activeFilters.filter(t=>t.type===e.dataset.type).length;e.querySelector(".fcount").textContent=t||"",e.classList.toggle("active",t>0)});const e=document.getElementById("filterCount");e&&(e.textContent=activeFilters.length,e.style.display=activeFilters.length?"":"none")}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"),n=activeFilters.length?`<button class="chip-clear" onclick="clearFilters()">${escapeHtml(t("filters.clearAll"))}</button>`:"";e.innerHTML=n+activeFilters.map((e,n)=>{let a=escapeHtml(e.value);return"priority"===e.type&&(a=priorityHtml(e.value)),"user"===e.type&&(a=escapeHtml(getUserFullFormat(e.value))),`<span class="chip" data-type="${e.type}">${a}<button onclick="removeFilter(${n})" title="${t("tooltip.delete")}" aria-label="${t("tooltip.delete")}">&times;</button></span>`}).join(""),syncFilterUi()}function matchesFilters(e){if(activeFilters.length>0){if(![...new Set(activeFilters.map(e=>e.type))].every(t=>activeFilters.filter(e=>e.type===t).some(n=>"tag"===t?e.tags.includes(n.value)||e.tags.includes("#"+n.value):"category"===t?e.category===n.value:"user"===t?e.assignees.some(e=>normalizeUserId(e)===n.value):"priority"===t&&e.priority===n.value)))return!1}if(globalSearchTerm){const t=globalSearchTerm.toLowerCase();if(!(e.id&&e.id.toLowerCase().includes(t)||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(){if(readOnly())return;const e=document.getElementById("columnsModal");document.getElementById("columnsList").innerHTML=config.columns.map((e,n)=>`\n <div class="row-card">\n <button onclick="moveColumn(${n}, -1)" ${0===n?"disabled":""} class="btn btn-ghost btn-sm btn-icon" title="${t("action.moveUp")}" aria-label="${t("action.moveUp")}">↑</button>\n <button onclick="moveColumn(${n}, 1)" ${n===config.columns.length-1?"disabled":""} class="btn btn-ghost btn-sm btn-icon" title="${t("action.moveDown")}" aria-label="${t("action.moveDown")}">↓</button>\n <input type="text" class="grow" value="${escapeHtml(e.name)}" onchange="updateColumn(${n}, 'name', this.value)">\n <input type="text" class="col-id" value="${escapeHtml(e.id)}" onchange="updateColumn(${n}, 'id', this.value)" placeholder="ID">\n <button onclick="deleteColumn(${n})" class="btn btn-ghost btn-sm btn-icon" title="${t("tooltip.delete")}" aria-label="${t("tooltip.delete")}"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M5 4.5l.5 8.5h5l.5-8.5"/></svg></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){n=n.trim();const a=config.columns[e];if("id"===t){if(!n||config.columns.some((t,a)=>a!==e&&t.id===n))return void openColumnsModal();[...tasks,...archivedTasks].forEach(e=>{e.status===a.id&&(e.status=n)})}a[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){if(readOnly())return;isEditMode=!!e;const a=document.getElementById("newTaskModal"),s=document.getElementById("newTaskForm"),r=a.querySelector("h2"),o=document.getElementById("taskFormSubmitBtn");r.textContent=t(isEditMode?"taskForm.editTask":"taskForm.newTask"),o.textContent=t(isEditMode?"taskForm.save":"taskForm.create"),ddSetOptions("taskStatus",config.columns.map(e=>({value:e.id,label:e.name}))),ddSetOptions("taskPriority",[{value:"",label:t("taskForm.priorityNone"),html:'<span class="prio prio-none"></span><span>'+escapeHtml(t("taskForm.priorityNone"))+"</span>"},...config.priorities.map(e=>({value:clean(e),label:priorityLabel(e),html:priorityIcon(e,'aria-hidden="true"')+"<span>"+escapeHtml(priorityLabel(e))+"</span>"}))]),updateAutocomplete(),isEditMode||ddSetValue("taskPriority","",!0),isEditMode?(document.getElementById("taskEditId").value=e.id,document.getElementById("taskTitle").value=e.title,ddSetValue("taskStatus",e.status,!0),ddSetValue("taskPriority",clean(e.priority||""),!0),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||[])),formBlockedBy=[...e.blockedBy||[]]):(s.reset(),document.getElementById("taskEditId").value="",n&&ddSetValue("taskStatus",n,!0),formSubtasks=[],formBlockedBy=[]),renderFormSubtasks(),renderBlockedByField(),a.classList.add("active")}function closeTaskModal(){document.getElementById("newTaskModal").classList.remove("active"),document.getElementById("newTaskForm").reset(),formSubtasks=[],formBlockedBy=[],isEditMode=!1}function renderFormSubtasks(){document.getElementById("formSubtasksList").innerHTML=formSubtasks.map((e,n)=>`\n <li class="subtask-item${e.completed?" done":""}">\n <input type="checkbox" ${e.completed?"checked":""} onchange="toggleFormSubtask(${n})">\n <span>${e.text.replace(/</g,"&lt;").replace(/>/g,"&gt;")}</span>\n <button type="button" class="del" onclick="deleteFormSubtask(${n})" title="${t("tooltip.delete")}" aria-label="${t("tooltip.delete")}"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M5 4.5l.5 8.5h5l.5-8.5"/></svg></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="",refreshHousekeepingState(),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(", ")}`;const n=backlogColumnId(),a=!readOnly();config.columns.forEach(s=>{const r=s.id===n,o=orderedTasks(tasks).filter(e=>e.status===s.id&&matchesFilters(e)),i=r?tasks.filter(e=>e.status===s.id).length:o.length;console.log(`Column ${s.id}: ${o.length} tasks`);const l=document.createElement("div");l.className=r?"kanban-column is-rail is-backlog":"kanban-column",l.dataset.columnId=s.id;const c=r?splitColumnIcon(s.name):null,d=c?`<span class="rail-title-icon">${escapeHtml(c.icon)}</span><span class="rail-title-words"> ${escapeHtml(c.words)} <span class="column-count">${i}</span></span>`:`${escapeHtml(s.name)}\n <span class="column-count">${i}</span>`;l.innerHTML=`\n <div class="column-header">\n <div class="column-title">\n ${d}\n </div>\n <div class="column-tools">\n ${a?`<button class="column-add-btn" aria-label="${escapeHtml(t("taskForm.newTask"))}"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg></button>`:""}\n </div>\n </div>\n <div class="task-list" ondrop="drop(event)" ondragover="allowDrop(event)">\n ${r||0!==o.length?"":`<div class="empty-state">${t("empty.noTasks")}</div>`}\n </div>\n `;const u=l.querySelector(".task-list"),p=l.querySelector(".column-add-btn");if(p&&(p.title=t("taskForm.newTask"),p.addEventListener("click",()=>openTaskModal(null,s.id))),r){u.appendChild(buildRailStub({kind:"backlog",name:c.words||s.name,count:i,note:t("backlog.stub",{file:resolveBacklogFile(config).file}),openLabel:t("backlog.open"),onOpen:openBacklogModal})),openRailOnTap(l,openBacklogModal);let e=0;l.addEventListener("dragenter",()=>{e++,l.classList.add("is-open")}),l.addEventListener("dragleave",()=>{e=Math.max(0,e-1),e||l.classList.remove("is-open")}),l.addEventListener("drop",()=>{e=0,l.classList.remove("is-open")})}else{const e=linkIndex();o.forEach(t=>{const n=createTaskElement(t,e);u.appendChild(n)})}e.appendChild(l)}),appendArchiveRail(e),updateHousekeepingBar()}function splitColumnIcon(e){const t=!/^[\u00A9\u00AE\u2122]/.test(e)&&/^((?:\p{Regional_Indicator}{2}|[\p{Emoji_Presentation}\p{Extended_Pictographic}])[\uFE0F\u200D\u{1F3FB}-\u{1F3FF}\p{Emoji_Presentation}\p{Extended_Pictographic}]*)\s*(.*)$/u.exec(e);return t?{icon:t[1],words:t[2]}:{icon:"",words:e}}setInterval(pollOnce,2e3),document.addEventListener("visibilitychange",()=>{"visible"===document.visibilityState&&pollOnce()}),document.addEventListener("click",e=>{const t=e.target.closest(".dd-trigger");if(t){e.stopPropagation();const n=t.closest(".dd");return void(n.classList.contains("open")?ddClose(n):(ddOpen(n),n.classList.contains("dd-field")&&ddFocusStart(n)))}const n=e.target.closest(".dd-field .dd-opt");n&&(e.stopPropagation(),ddPick(n))},!0),document.addEventListener("mousedown",e=>{e.target.closest(".dd")||ddCloseAll()},!0),document.addEventListener("keydown",e=>{const t=e.target.closest(".dd");if(!t)return;const n=t.classList.contains("open"),a=t.querySelector(".dd-trigger"),s=ddOptionEls(t),r=s.indexOf(document.activeElement);if("Escape"===e.key&&n)e.stopPropagation(),ddClose(t),a.focus();else if("ArrowDown"===e.key||"ArrowUp"===e.key){if(e.preventDefault(),!n&&r<0)return ddOpen(t),void ddFocusStart(t);const a="ArrowDown"===e.key?1:-1;s[Math.min(Math.max(r+a,0),s.length-1)]?.focus()}else("Enter"===e.key||" "===e.key)&&n&&t.classList.contains("dd-field")&&r>=0?(e.preventDefault(),ddPick(s[r])):"Tab"===e.key&&n&&t.classList.contains("dd-field")&&ddClose(t)},!0);let railOpenedByKeyboard=null;function restoreRailFocus(e){const t=railOpenedByKeyboard;if(railOpenedByKeyboard=null,t!==e)return;const n=document.querySelector(`.kanban-column.is-${e} .rail-button`);n&&n.focus()}function openRailOnTap(e,t){e.addEventListener("click",e=>{e.target.closest("button")||t()})}function buildRailStub({kind:e,name:n,count:a,note:s,openLabel:r,onOpen:o}){const i=document.createElement("div");i.className="rail-stub";const l=document.createElement("button");l.type="button",l.className="rail-button",l.setAttribute("aria-label",`${n}: ${r}`);const c=document.createElement("span");c.className="rail-count",c.textContent=String(a);const d=document.createElement("span");d.className="rail-name",d.textContent=n,l.append(c,d),l.addEventListener("click",t=>{railOpenedByKeyboard=0===t.detail?e:null,o()});const u=document.createElement("div");u.className="rail-stub-body";const p=document.createElement("div");p.className="rail-stub-count",p.textContent=String(a);const h=document.createElement("div");h.className="rail-stub-note",h.textContent=s;const m=document.createElement("div");return m.className="rail-stub-hint",m.textContent=t("rail.hint"),u.append(p,h,m),i.append(l,u),i}function appendArchiveRail(e){if(!archivedTasks.length)return;const n=t("header.archives"),a=document.createElement("div");a.className="kanban-column is-rail is-archive",a.innerHTML=`\n <div class="column-header">\n <div class="column-title">\n <span class="rail-title-icon">🗃️</span><span class="rail-title-words"> ${escapeHtml(n)} <span class="column-count">${archivedTasks.length}</span></span>\n </div>\n </div>\n <div class="task-list"></div>`,a.querySelector(".task-list").appendChild(buildRailStub({kind:"archive",name:n,count:archivedTasks.length,note:t("archive.stub"),openLabel:t("archive.open"),onOpen:openArchiveModal})),openRailOnTap(a,openArchiveModal);const s=()=>setTimeout(()=>{const e=document.getElementById("kanbanView"),t=a.getBoundingClientRect().right-e.getBoundingClientRect().right;t>0&&e.scrollBy({left:t+8,behavior:"smooth"})},200);a.addEventListener("mouseenter",s),a.addEventListener("focusin",s),e.appendChild(a)}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 s=Math.round((n-a)/864e5);return s<0?"overdue":s<=2?"soon":null}function createTaskElement(e,n=linkIndex()){const a=document.createElement("div");a.className="task-card",hkState.staleIds.has(e.id)&&(a.classList.add("hk-stale"),a.title=t("hk.staleCard"));const s=!readOnly();a.draggable=s,a.dataset.taskId=e.id;const r=dueStatus(e);r&&a.classList.add(`task-card--${r}`);const o=e.subtasks.filter(e=>e.completed).length,i=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: ${o/e.subtasks.length*100}%"></div>\n </div>\n <span>${o}/${e.subtasks.length}</span>\n </div>\n </div>`:"",l=blockedBadgeHtml(e,n),c=e.due?`<span class="badge badge-due${r?` badge-due--${r}`:""}" title="${escapeHtml(t("overdue"===r?"tooltip.overdue":"soon"===r?"tooltip.dueSoon":"meta.due"))}">${escapeHtml(e.due)}</span>`:"";a.innerHTML=`\n <div class="task-header">\n ${e.priority?`${priorityIcon(e.priority,`data-filter="priority" data-filter-value="${escapeHtml(e.priority)}" title="${escapeHtml(priorityLabel(e.priority))}" role="img" aria-label="${escapeHtml(priorityLabel(e.priority))}"`)}`:""}\n <span class="task-id">${escapeHtml(e.id)}</span>\n ${s?`<button class="task-edit-btn" data-edit-task="1" title="${escapeHtml(t("taskDetail.edit"))}" aria-label="${escapeHtml(t("taskDetail.edit"))}"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M10.5 2.5l3 3L5 14H2v-3z"/></svg></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 ${l}\n ${c}\n ${e.category?`<span class="badge" data-filter="category" data-filter-value="${escapeHtml(e.category)}" title="${t("tooltip.filterByCategory")}">${escapeHtml(e.category)}</span>`:""}\n ${e.assignees.map(e=>`<span class="badge" data-filter="user" data-filter-value="${escapeHtml(normalizeUserId(e))}" title="${t("tooltip.filterByUser")}">${escapeHtml(e)}</span>`).join("")}\n ${e.tags.map(e=>`<span class="tag" data-filter="tag" data-filter-value="${escapeHtml(e)}" title="${t("tooltip.filterByTag")}">${escapeHtml(e)}</span>`).join("")}\n </div>\n ${i}\n `;const d=a.querySelector("[data-edit-task]");return d&&d.addEventListener("click",t=>{t.stopPropagation(),openTaskModal(e)}),a.querySelectorAll("[data-filter]").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation(),clickToFilter(e.dataset.filter,e.dataset.filterValue)})}),s&&a.addEventListener("dragstart",drag),a.addEventListener("click",()=>showTaskDetail(e)),s&&attachTouchDrag(a),a}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}async function moveTask(e,t,n=null,{askOrder:a=!0}={}){if(readOnly())return!1;await autoSave();const s=tasks.find(t=>t.id===e);if(!s)return!1;const r=t||s.status,o=n?{type:"moveTask",id:e,status:r,before:n}:{type:"moveTask",id:e,status:r,atEnd:!0},i=await readProjectFiles();try{if(!applyOps({board:i.board,archive:i.archive,backlog:i.backlog},[o]).files.length)return!1}catch(e){}const l=[o];a&&r!==backlogColumnId()&&!orderingActive()&&"on"===resolveOrderChoice()&&l.unshift({type:"sortTasks",mode:"positions"});const c=await commit(l,{explicit:!0});return c.ok&&c.changed}function drop(e){if(e.preventDefault(),readOnly())return;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 s=a.closest(".kanban-column").dataset.columnId,r=getDropBeforeId(a,e.clientY,n),o=document.querySelector(`[data-task-id="${n}"]`);o&&o.classList.remove("dragging"),moveTask(n,s,r).then(e=>{e&&showNotification(t("notif.taskMoved"),"success")})}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:not(.is-archive)");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),s=a&&a.closest(".kanban-column");if(document.querySelectorAll(".kanban-column").forEach(e=>e.classList.remove("drop-target")),s){const e=s.dataset.columnId,a=getDropBeforeId(s.querySelector(".task-list"),n.clientY,touchDrag.taskId);moveTask(touchDrag.taskId,e,a).then(e=>{e&&showNotification(t("notif.taskMoved"),"success")})}}touchDrag=null}};e.addEventListener("touchend",n,{passive:!1}),e.addEventListener("touchcancel",n,{passive:!1})}function orderingActive(){return tasks.some(e=>null!==e.order&&void 0!==e.order)}function orderChoiceKey(){return"kanbanmd.orderChoice."+(source?source.key:"")}function getOrderChoice(){try{return localStorage.getItem(orderChoiceKey())}catch(e){return null}}function setOrderChoice(e){try{localStorage.setItem(orderChoiceKey(),e)}catch(e){}}function resolveOrderChoice(){const e=getOrderChoice();if("on"===e||"off"===e)return e;const n=confirm(t("confirm.rememberOrder"))?"on":"off";return setOrderChoice(n),n}async function runOrderOp(e,n){if(readOnly())return!1;await autoSave();const a=await commit([e],{explicit:!0});return a.ok&&n&&showNotification(t(n),"success"),a.ok}async function toggleManualOrder(){if(!readOnly())if(orderingActive()){if(!confirm(t("confirm.orderOff")))return;setOrderChoice("off"),await runOrderOp({type:"sortTasks",mode:"clear"},"notif.orderOff")}else setOrderChoice("on"),await runOrderOp({type:"sortTasks",mode:"positions"},"notif.orderOn")}async function resetOrderById(){readOnly()||confirm(t("confirm.resetOrder"))&&await runOrderOp({type:"sortTasks",mode:"ids"},"notif.orderReset")}function updateOrderingMenu(){const e=document.getElementById("orderToggleBtn"),n=document.getElementById("orderResetBtn");if(!e||!n)return;const a=boardLoaded()&&!readOnly();e.style.display=a?"inline-flex":"none",n.style.display=a&&orderingActive()?"inline-flex":"none",e.querySelector(".mi-label").textContent=t(orderingActive()?"menu.manualOrderOn":"menu.manualOrderOff"),n.querySelector(".mi-label").textContent=t("menu.resetOrderById"),"function"==typeof updateBacklogMenu&&updateBacklogMenu()}function showTaskDetail(e,n={}){detailCtx={archived:!!n.archived,fromArchivePanel:!!n.fromArchivePanel},currentDetailTask=e;const a=document.getElementById("taskModalActions"),s=document.getElementById("taskModal"),r=document.getElementById("modalBody"),o=document.getElementById("archiveModal");let i="";const l=`<button class="btn btn-secondary" onclick="openShareDialog()">${t("share.button")}</button>`,c=!readOnly()&&!detailCtx.archived;detailCtx.fromArchivePanel&&o.classList.remove("active"),i=c?`<div class="push">\n <button class="btn btn-danger" onclick="deleteCurrentTask()">${t("taskDetail.delete")}</button>\n <button class="btn btn-secondary" onclick="archiveCurrentTask()">${t("taskDetail.archive")}</button>\n </div>\n ${l}\n <button class="btn btn-primary" onclick="editCurrentTask()">${t("taskDetail.edit")}</button>`:`<div class="push"></div>${l}`,a.innerHTML=i;const d=config.columns.find(t=>t.id===e.status),u=d?d.name:e.status;r.innerHTML=`\n <div class="detail">\n ${detailBackHtml()}\n <div>\n <div class="detail-id">${escapeHtml(e.id)}</div>\n <h3>${escapeHtml(e.title)}</h3>\n </div>\n\n <dl class="detail-meta">\n ${e.priority?`<div><dt>${t("meta.priority")}</dt><dd>${priorityHtml(e.priority)}</dd></div>`:""}\n <div><dt>${t("meta.status")}</dt><dd>${escapeHtml(u)}</dd></div>\n ${e.category?`<div><dt>${t("meta.category")}</dt><dd>${escapeHtml(e.category)}</dd></div>`:""}\n ${e.assignees.length>0?`<div><dt>${t("meta.assigned")}</dt><dd>${escapeHtml(e.assignees.join(", "))}</dd></div>`:""}\n ${e.created?`<div><dt>${t("meta.created")}</dt><dd>${e.created}</dd></div>`:""}\n ${e.started?`<div><dt>${t("meta.started")}</dt><dd>${e.started}</dd></div>`:""}\n ${e.due?`<div><dt>${t("meta.due")}</dt><dd>${e.due}</dd></div>`:""}\n ${e.completed?`<div><dt>${t("meta.completed")}</dt><dd>${e.completed}</dd></div>`:""}\n </dl>\n\n ${e.tags.length>0?`\n <div>\n <div class="detail-label">${t("meta.tags")}</div>\n <div class="detail-tags">${e.tags.map(e=>`<span>${escapeHtml(e)}</span>`).join("")}</div>\n </div>\n `:""}\n\n ${linksSectionHtml(e,c)}\n\n ${e.description?`\n <div>\n <div class="detail-label">${t("meta.description")}</div>\n <div class="detail-text">${markdownToHtml(e.description)}</div>\n </div>\n `:""}\n\n ${c||e.subtasks.length?`<div>\n <div class="detail-label">${t("meta.subtasks",{completed:e.subtasks.filter(e=>e.completed).length,total:e.subtasks.length})}</div>\n <ul id="subtasksList" class="subtask-list">\n ${e.subtasks.map((n,a)=>{const s=n.text.replace(/</g,"&lt;").replace(/>/g,"&gt;");return`\n <li class="subtask-item${n.completed?" done":""}">\n <input type="checkbox" ${n.completed?"checked":""} ${c?`onchange="toggleSubtask('${e.id}', ${a})"`:"disabled"}>\n <span ${c?`ondblclick="editSubtask('${e.id}', ${a})" title="${t("tooltip.doubleClickEdit")}"`:""}>${s}</span>\n ${c?`<button class="del" onclick="deleteSubtask('${e.id}', ${a})" title="${t("tooltip.delete")}" aria-label="${t("tooltip.delete")}"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M5 4.5l.5 8.5h5l.5-8.5"/></svg></button>`:""}\n </li>\n `}).join("")}\n </ul>\n ${c?`\n <div class="subtask-add">\n <input type="text" id="newSubtaskInput" placeholder="${t("subtask.newPlaceholder")}" onkeypress="if(event.key==='Enter') addSubtask('${e.id}')">\n <button onclick="addSubtask('${e.id}')" class="btn btn-secondary">${t("taskForm.subtaskAdd")}</button>\n </div>\n `:""}\n </div>`:""}\n\n ${e.notes?`\n <div>\n <div class="detail-label">${t("meta.notes")}</div>\n <div class="detail-notes">${markdownToHtml(e.notes)}</div>\n </div>\n `:""}\n </div>\n `,s.classList.add("active")}function closeModal(){document.getElementById("taskModal").classList.remove("active"),detailStack=[],detailCtx.fromArchivePanel&&(document.getElementById("archiveModal").classList.add("active"),detailCtx.fromArchivePanel=!1)}function editCurrentTask(){readOnly()||currentDetailTask&&(closeModal(),openTaskModal(currentDetailTask))}function toggleSubtask(e,t){if(readOnly())return;const n=tasks.find(t=>t.id===e);n&&n.subtasks[t]&&(n.subtasks[t].completed=!n.subtasks[t].completed,currentDetailTask=n,showTaskDetail(n,detailCtx),renderKanban(),autoSave())}function deleteSubtask(e,n){if(readOnly())return;const a=tasks.find(t=>t.id===e);a&&confirm(t("confirm.deleteSubtask"))&&(a.subtasks.splice(n,1),currentDetailTask=a,showTaskDetail(a,detailCtx),renderKanban(),autoSave())}function addSubtask(e){if(readOnly())return;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,detailCtx),renderKanban(),autoSave())}function editSubtask(e,n){if(readOnly())return;const a=tasks.find(t=>t.id===e);if(!a||!a.subtasks[n])return;const s=prompt(t("prompt.editSubtask"),a.subtasks[n].text);null!==s&&s.trim()&&(a.subtasks[n].text=s.trim(),currentDetailTask=a,showTaskDetail(a,detailCtx),renderKanban(),autoSave())}document.getElementById("newTaskForm").addEventListener("submit",async e=>{e.preventDefault();const n=document.getElementById("taskTitle").value.trim(),a=document.getElementById("taskStatus").value,s=document.getElementById("taskPriority").value,r=document.getElementById("taskCategory").value.trim(),o=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,u=document.getElementById("taskTags").value.trim(),p=document.getElementById("taskDescription").value.trim(),h=document.getElementById("taskNotes").value.trim(),m=u?u.split(/\s+/).filter(e=>e.startsWith("#")||e).map(e=>e.startsWith("#")?e:"#"+e):[],g=o?o.split(",").map(e=>e.trim()):[];if(isEditMode){const e=document.getElementById("taskEditId").value,o=tasks.find(t=>t.id===e);o&&(o.title=n,o.status=a,o.priority=s,o.category=r,o.assignees=g,o.tags=m,o.created=i,o.started=l,o.due=c,o.completed=d,o.description=p,o.subtasks=formSubtasks,o.blockedBy=[...formBlockedBy],o.notes=h,showNotification(t("notif.taskEdited",{id:e}),"success"))}else{const e=[...tasks,...archivedTasks].reduce((e,t)=>Math.max(e,parseInt((t.id.match(/(\d+)$/)||[])[1])||0),0);config.lastTaskId=Math.max(config.lastTaskId,e)+1;const o="TASK-"+String(config.lastTaskId).padStart(3,"0");tasks.push({id:o,title:n,status:a,priority:s,category:r,assignees:g,tags:m,blockedBy:[...formBlockedBy],created:i||(new Date).toISOString().split("T")[0],started:l||"",due:c,completed:d||"",description:p,subtasks:formSubtasks,notes:h}),showNotification(t("notif.taskCreated",{id:o}),"success")}closeTaskModal(),renderKanban(),autoSave()});let formBlockedBy=[];function linkIndex(){const e=backlogColumnId();return indexTasks({board:tasks.filter(t=>t.status!==e),backlog:e?tasks.filter(t=>t.status===e):[],archive:archivedTasks})}function blockersNow(e,t=linkIndex()){return blockersOf(e,t,config.columns)}const LOCK_SVG='<svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M4 7.5h8v6H4zM5.5 7.5V5.5a2.5 2.5 0 015 0v2"/></svg>',COLUMN_ICON_RE=/^((?:\p{Regional_Indicator}{2})|(?:\p{Extended_Pictographic}(?:\uFE0F|[\u{1F3FB}-\u{1F3FF}]|\u200D\p{Extended_Pictographic})*))\s*/u;function columnParts(e){const t=config.columns.find(t=>t.id===e.status),n=t?t.name:e.status,a=COLUMN_ICON_RE.exec(n);return a&&!/^[\u00A9\u00AE\u2122]/.test(a[1])&&n.length>a[0].length?{icon:a[1],label:n.slice(a[0].length)}:{icon:"",label:n}}function linkStateOf(e){return"archive"===e.where?{icon:"🗃️",label:t("links.archived")}:columnParts(e.task)}function blockedBadgeHtml(e,n){if(!e.blockedBy||!e.blockedBy.length)return"";if(config.columns.length&&e.status===config.columns[config.columns.length-1].id)return"";const a=blockersNow(e,n).open;if(!a.length)return"";const s=t("links.badge",{count:a.length,list:a.map(e=>`${linkStateOf(e).icon} ${e.task.id} ${e.task.title}`.trim()).join(", ")});return`<span class="badge badge-blocked" title="${escapeHtml(s)}" role="img" aria-label="${escapeHtml(s)}">${LOCK_SVG}${a.length}</span>`}function autolinkTaskIds(e){if(!/TASK-\d+/.test(e))return e;const t=linkIndex();let n=!1;return e.split(/(<[^>]*>)/).map(e=>e.startsWith("<")?(/^<a[\s>]/i.test(e)?n=!0:/^<\/a>/i.test(e)&&(n=!1),e):n?e:e.replace(/\bTASK-\d+\b/g,e=>{const n=t.get(e);return n?`<a href="#" class="task-link" data-task-link="${e}" title="${escapeHtml(n.task.title)}">${e}</a>`:e})).join("")}function openLinkedTask(e){const t=linkIndex().get(e);t&&(currentDetailTask&&currentDetailTask.id!==e&&(detailStack=[...detailStack,{id:currentDetailTask.id,archived:detailCtx.archived}].slice(-20)),showTaskDetail(t.task,{archived:"archive"===t.where,fromArchivePanel:detailCtx.fromArchivePanel}))}function detailBackHtml(){if(!detailStack.length)return"";const{id:e,archived:n}=detailStack[detailStack.length-1],a=(n?archivedTasks:tasks).find(t=>t.id===e),s=a?linkStateOf({task:a,where:n?"archive":"board"}):null,r=s?`${s.icon} ${s.label}`.trim():"",o=a?`${t("links.back",{id:e})}, ${a.title}, ${r}`:t("links.back",{id:e});return`<button type="button" class="detail-back" data-detail-back title="${escapeHtml(o)}" aria-label="${escapeHtml(o)}">\n <span class="detail-back-arrow" aria-hidden="true">←</span>\n <span class="detail-back-text"><span class="detail-back-title">${escapeHtml(a?a.title:e)}</span><span class="detail-back-meta">${escapeHtml(r?`${e} · ${r}`:e)}</span></span>\n </button>`}function detailBack(){for(;detailStack.length;){const{id:e,archived:t}=detailStack[detailStack.length-1];detailStack=detailStack.slice(0,-1);const n=(t?archivedTasks:tasks).find(t=>t.id===e);if(n){showTaskDetail(n,{archived:t,fromArchivePanel:detailCtx.fromArchivePanel});const e=document.querySelector("#modalBody h3");return void(e&&(e.tabIndex=-1,e.focus()))}}closeModal()}function linkChipHtml(e,n){if(!n)return`<span class="link-chip link-chip--muted" title="${escapeHtml(t("links.unknown"))}"><span class="link-ic" aria-hidden="true">❔</span>${escapeHtml(e)}</span>`;const a=linkStateOf(n),s="archive"===n.where||isFinished(n,config.columns),r=`${n.task.title} · ${a.label}`,o=a.icon?`<span class="link-ic" aria-hidden="true">${a.icon}</span>`:'<span class="link-ic link-dot" aria-hidden="true"></span>';return`<a href="#" class="link-chip${s?" link-chip--muted":""}" data-task-link="${escapeHtml(e)}" title="${escapeHtml(r)}">${o}${escapeHtml(e)}</a>`}function linksSectionHtml(e,n){const a=linkIndex(),s=(e.blockedBy||[]).map(e=>({id:e,entry:a.get(e)})),r=blockedByThis(e.id,a);if(!s.length&&!r.length)return"";const o=n?s.filter(e=>e.entry&&"backlog"===e.entry.where).map(e=>`<div class="link-banner" role="status">${escapeHtml(t("links.backlogNote",{id:e.id}))} <button type="button" class="link-action" data-promote-blocker="${escapeHtml(e.id)}">${escapeHtml(t("links.moveToBoard"))}</button></div>`).join(""):"",i=hasCycle(e.id,a)?`<div class="link-warn">${escapeHtml(t("links.cycle"))}</div>`:"";return`\n ${s.length?`<div><div class="detail-label">${escapeHtml(t("links.blockedBy"))}</div><div class="link-chips">${s.map(e=>linkChipHtml(e.id,e.entry)).join("")}</div>${o}${i}</div>`:""}\n ${r.length?`<div><div class="detail-label">${escapeHtml(t("links.blocks"))}</div><div class="link-chips">${r.map(e=>linkChipHtml(e.task.id,e)).join("")}</div></div>`:""}`}async function promoteBlocker(e){if(readOnly())return;const n=backlogColumnId(),a=config.columns.find(e=>e.id!==n);if(!a)return;await moveTask(e,a.id,null,{askOrder:!1})&&(showNotification(t("notif.taskMoved"),"success"),currentDetailTask&&showTaskDetail(currentDetailTask,detailCtx))}function renderBlockedByField(){const e=document.getElementById("taskBlockedByField");if(!e)return;const n=linkIndex(),a=isEditMode?document.getElementById("taskEditId").value:"",s=[...n.values()].filter(e=>"archive"!==e.where&&e.task.id!==a&&!formBlockedBy.includes(e.task.id)).sort((e,t)=>e.task.id.localeCompare(t.task.id));e.innerHTML=`\n <label for="taskBlockedBy">${escapeHtml(t("links.blockedBy"))}</label>\n <div class="chips">${formBlockedBy.map(e=>{const a=n.get(e);return`<span class="chip"><span class="task-id">${escapeHtml(e)}</span>${a?` ${escapeHtml(a.task.title)}`:` ${escapeHtml(t("links.unknown"))}`}<button type="button" data-unblock="${escapeHtml(e)}" aria-label="${escapeHtml(t("tooltip.delete")+" "+e)}">&times;</button></span>`}).join("")}</div>\n <input type="text" id="taskBlockedBy" list="blockedByList" placeholder="${escapeHtml(t("links.add"))}" autocomplete="off">\n <datalist id="blockedByList">${s.map(e=>`<option value="${escapeHtml(e.task.id)}">${escapeHtml(e.task.title)}</option>`).join("")}</datalist>\n <small>${escapeHtml(t("links.help"))}</small>\n <small id="taskBlockedByError" class="link-error" role="alert" hidden></small>`;const r=document.getElementById("taskBlockedBy"),o=document.getElementById("taskBlockedByError"),i=(e,n)=>{o.textContent=t(e,{q:n}),o.hidden=!1,r.setAttribute("aria-invalid","true"),r.setAttribute("aria-describedby","taskBlockedByError")},l=()=>{const e=r.value.trim();if(!e)return;const t=/^\d+$/.test(e)?"TASK-"+e:e;let n=s.filter(e=>e.task.id===(parseTaskIds(t)[0]||""));return n.length||(n=s.filter(t=>t.task.title.toLowerCase().includes(e.toLowerCase()))),n.length?n.length>1?i("links.severalMatches",e):(formBlockedBy.push(n[0].task.id),renderBlockedByField(),void document.getElementById("taskBlockedBy").focus()):i("links.noMatch",e)};r.addEventListener("input",()=>{o.hidden=!0,r.removeAttribute("aria-invalid"),r.removeAttribute("aria-describedby")}),r.addEventListener("change",l),r.addEventListener("keydown",e=>{"Enter"===e.key&&(e.preventDefault(),l())}),e.querySelectorAll("[data-unblock]").forEach(e=>e.addEventListener("click",()=>{formBlockedBy=formBlockedBy.filter(t=>t!==e.dataset.unblock),renderBlockedByField()}))}async function loadArchive(){console.log("🗃️ === LOADING ARCHIVE ===");try{const e=await source.read("archive.md");if(!e)return console.log("No archive.md"),void(archivedTasks=[]);const t=e.text;console.log("✓ Archive content read, length:",t.length,"characters"),console.log("Archive content preview:",t.substring(0,500));const n=parseArchive(t,config.columns);loadedArchiveVersion=n.formatVersion,archivedTasks=n.tasks,console.log("✓ Archive parsed. Total archived tasks:",archivedTasks.length);const a=(t.match(/^###\s+TASK-/gm)||[]).length,s=new Set(archivedTasks.map(e=>e.id)).size;if(source.writable)if(loadedArchiveVersion<3&&archivedTasks.length>0&&archivedTasks.length===a&&s===a)try{await backupOriginal("archive.md",t,loadedArchiveVersion)?(await writeWholeArchive(),console.log("Migrated archive.md to v3 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 loadedArchiveVersion<3&&archivedTasks.length>0&&console.warn(`Skipped archive.md migration: incomplete parse (${archivedTasks.length}/${a} blocks). File left unchanged.`);else;}catch(e){console.error("❌ archive.md not found or error:",e),archivedTasks=[]}}function saveArchive(){return autoSave()}function archiveCurrentTask(){if(!readOnly()&&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(){readOnly()||currentDetailTask&&deleteTask(currentDetailTask.id,!1)}function deleteTask(e,n=!1){if(readOnly())return;const a=n?archivedTasks:tasks,s=a.find(t=>t.id===e);if(!s)return;const r=t(n?"confirm.deleteTask":"confirm.deleteTaskFromArchive",{title:s.title});if(confirm(r)){const s=a.findIndex(t=>t.id===e);s>=0&&(a.splice(s,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 reloadFromDisk({quiet:!0}),renderArchiveList(),document.getElementById("archiveModal").classList.add("active"),console.log("✓ Archive modal opened")}function closeArchiveModal(){document.getElementById("archiveModal").classList.remove("active"),restoreRailFocus("archive")}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.id.toLowerCase().includes(e.toLowerCase())||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=`<div class="empty-state">${t("archives.empty")}</div>`);console.log("✓ Rendering",a.length,"tasks");const s=!readOnly();n.innerHTML=a.map(e=>`\n <div class="archive-item" style="cursor: pointer;" data-id="${escapeHtml(e.id)}">\n <div class="top">\n <div>\n <span class="task-id">${escapeHtml(e.id)}</span>\n <strong style="margin-left: var(--s-2);">${escapeHtml(e.title)}</strong>\n </div>\n ${s?`<div style="display: flex; gap: var(--s-2);">\n <button data-action="delete" class="btn btn-danger btn-sm" title="${t("tooltip.delete")}" aria-label="${t("tooltip.delete")}"><svg class="ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4.5h10M6.5 4.5V3h3v1.5M5 4.5l.5 8.5h5l.5-8.5"/></svg></button>\n <button data-action="restore" class="btn btn-secondary btn-sm">${t("action.restore")}</button>\n </div>`:""}\n </div>\n ${e.description?`<div class="desc">${markdownToHtml(e.description)}</div>`:""}\n <div class="task-meta">\n ${e.priority?`<span class="badge">${priorityHtml(e.priority)}</span>`:""}\n ${e.category?`<span class="badge">${escapeHtml(e.category)}</span>`:""}\n ${e.tags.map(e=>`<span class="tag">${escapeHtml(e)}</span>`).join("")}\n </div>\n </div>\n `).join("")}function onArchiveListClick(e){const t=e.target.closest(".archive-item");if(!t)return;const n=t.dataset.id,a=e.target.closest("[data-action]")?.dataset.action;"delete"===a?deleteTask(n,!0):"restore"===a?unarchiveTask(n):showTaskDetail(archivedTasks.find(e=>e.id===n),{archived:!0,fromArchivePanel:!0})}function unarchiveTask(e){if(readOnly())return;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({addSourceComment:e=!1}={}){Object.assign(config,normalizeConfig(config));const t=resolveBacklogFile(config);return generateBoard({config:config,tasks:t?tasks.filter(e=>e.status!==t.column):tasks},{sourceComment:e||hasSourceComment(currentKanbanContent)})}function archiveStrings(){return{title:t("markdown.archiveTitle"),desc:t("markdown.archiveDesc"),section:t("markdown.archiveSection")}}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)}function closeMenus(){["filterPanel","moreMenu","themeMenu"].forEach(e=>document.getElementById(e).classList.remove("open")),["filterToggleBtn","moreMenuBtn","themeMenuBtn"].forEach(e=>document.getElementById(e).setAttribute("aria-expanded","false")),ddCloseAll()}function togglePopover(e,t,n){n.stopPropagation();const a=document.getElementById(e),s=a.classList.contains("open");closeMenus(),s||(a.classList.add("open"),document.getElementById(t).setAttribute("aria-expanded","true"))}function toggleFilterPanel(e){togglePopover("filterPanel","filterToggleBtn",e)}function tidyMenuSeparators(){const e=[...document.getElementById("moreMenu").children];let t=!1,n=null;for(const a of e)a.classList.contains("menu-sep")?(a.style.display="none",t&&(n=a)):"none"!==a.style.display&&(n&&(n.style.display="",n=null),t=!0)}function toggleMoreMenu(e){tidyMenuSeparators(),togglePopover("moreMenu","moreMenuBtn",e)}function toggleThemeMenu(e){togglePopover("themeMenu","themeMenuBtn",e)}document.addEventListener("click",e=>{if(e.target.closest("[data-detail-back]"))return e.preventDefault(),e.stopPropagation(),void detailBack();const t=e.target.closest("[data-task-link]");if(t)return e.preventDefault(),e.stopPropagation(),void openLinkedTask(t.dataset.taskLink);const n=e.target.closest("[data-promote-blocker]");n&&(e.preventDefault(),e.stopPropagation(),promoteBlocker(n.dataset.promoteBlocker))},!0),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),document.addEventListener("ddopen",e=>{e.target.closest(".fdrop")&&["moreMenu","themeMenu"].forEach(e=>document.getElementById(e).classList.remove("open"))}),document.addEventListener("click",e=>{e.target.closest(".dd-menu")||closeMenus()}),document.addEventListener("keydown",e=>{"Escape"===e.key&&(closeMenus(),document.getElementById("shareModal").classList.contains("active")?closeShareDialog():document.getElementById("taskModal").classList.contains("active")?closeModal():document.getElementById("columnsModal").classList.contains("active")?closeColumnsModal():document.getElementById("serveHelpModal").classList.contains("active")?closeServeHelp():document.getElementById("pickNoteModal").classList.contains("active")?closePickNote():document.getElementById("archiveModal").classList.contains("active")?closeArchiveModal():document.getElementById("backlogModal").classList.contains("active")&&closeBacklogModal())}),document.addEventListener("mousedown",e=>{"shareModal"===e.target.id?closeShareDialog():"taskModal"===e.target.id?closeModal():"columnsModal"===e.target.id?closeColumnsModal():"archiveModal"===e.target.id?closeArchiveModal():"backlogModal"===e.target.id&&closeBacklogModal()}),function(){const e=document.getElementById("kanbanView");e.addEventListener("scroll",()=>e.classList.toggle("is-scrolled",e.scrollTop>0),{passive:!0})}();const HK_SNOOZE_MS=6048e5;let hkState={suggestions:[],staleIds:new Set};function hkKey(e){return"kanbanmd.hk."+e+"."+(source?source.key:"")}function hkFingerprint(e){return e.map(e=>e.kind+":"+e.count).join("|")}function hkHidden(e){try{if(parseInt(localStorage.getItem(hkKey("snooze"))||"0",10)>Date.now())return!0;if(localStorage.getItem(hkKey("dismissed"))===hkFingerprint(e))return!0}catch(e){}return!1}function refreshHousekeepingState(){if(hkState={suggestions:[],staleIds:new Set},boardLoaded())try{const e={board:currentKanbanContent,archive:null,backlog:currentBacklogText};hkState.suggestions=getHousekeeping(e).suggestions,hkState.staleIds=new Set(staleBacklogIds(e))}catch(e){console.warn("Housekeeping check skipped:",e&&e.message)}}function updateHousekeepingBar(){const e=document.getElementById("housekeepingBar"),n=document.getElementById("housekeepingBtn");if(!e)return;const a=boardLoaded()&&!readOnly();n&&(n.style.display=a?"inline-flex":"none",n.querySelector(".mi-label").textContent=t("hk.menu"));const s=hkState.suggestions;if(!a||!s.length||hkHidden(s))return e.style.display="none",void(e.textContent="");e.textContent="";const r=document.createElement("div");r.className="hk-msgs",s.forEach(e=>{const n=document.createElement("span");n.textContent=t("hk."+e.kind,{count:e.count,days:e.afterDays,max:e.max}),r.appendChild(n)});const o=document.createElement("div");o.className="hk-actions";const i=(e,t,n)=>{const a=document.createElement("button");a.type="button",a.className="btn "+n+" btn-sm",a.textContent=e,a.addEventListener("click",t),o.appendChild(a)},l=s.find(e=>"archive_done"===e.kind);l&&i(t("hk.archiveBtn",{count:l.count}),hkArchiveDone,"btn-primary");const c=s.find(e=>"backlog_large"===e.kind);c&&i(t("hk.splitBtn"),()=>hkSplitBacklog(c),"btn-primary"),i(t("hk.snooze"),hkSnooze,"btn-secondary"),i(t("hk.dismiss"),hkDismiss,"btn-secondary"),e.appendChild(r),e.appendChild(o),e.style.display="flex"}async function hkArchiveDone(){if(readOnly())return;const e=archiveCandidates({board:currentKanbanContent,archive:null,backlog:currentBacklogText});if(!e.length)return;if(!confirm(t("hk.confirmArchive",{count:e.length})))return;await autoSave();(await commit([{type:"archiveTasks",ids:e}],{explicit:!0})).ok&&showNotification(t("hk.archived",{count:e.length}),"success")}async function hkSplitBacklog(e){confirm(t("backlog.confirmOn",{count:e.count,column:e.column}))&&await runOrderOp({type:"setBacklogFile",enabled:!0,column:e.column},"backlog.onDone")}function hkSnooze(){try{localStorage.setItem(hkKey("snooze"),String(Date.now()+6048e5))}catch(e){}updateHousekeepingBar()}function hkDismiss(){try{localStorage.setItem(hkKey("dismissed"),hkFingerprint(hkState.suggestions))}catch(e){}updateHousekeepingBar()}async function editHousekeepingSettings(){if(readOnly())return;const e=formatHousekeepingSpec(config.housekeeping),n=prompt(t("hk.settingsPrompt"),e);if(null===n)return;const a=n.trim();let s;if(""===a||"reset"===a.toLowerCase())s={type:"setHousekeeping",reset:!0};else if("off"===a.toLowerCase())s={type:"setHousekeeping",off:!0};else{const e=parseHousekeepingSpec(a)||{};s={type:"setHousekeeping",done:e.done,backlog:e.backlog,max:e.max}}await autoSave();if((await commit([s],{explicit:!0})).ok){try{localStorage.removeItem(hkKey("snooze")),localStorage.removeItem(hkKey("dismissed"))}catch(e){}refreshHousekeepingState(),refreshUi(),showNotification(t("hk.saved"),"success")}}function backlogColumnId(){const e=resolveBacklogFile(config);return e?e.column:null}function backlogTasksNow(){const e=backlogColumnId();return e?orderedTasks(tasks).filter(t=>t.status===e):[]}function openBacklogModal(){backlogColumnId()&&(document.getElementById("backlogSearch").value="",renderBacklogList(),document.getElementById("backlogModal").classList.add("active"))}function closeBacklogModal(){document.getElementById("backlogModal").classList.remove("active"),restoreRailFocus("backlog")}function renderBacklogList(e=""){const n=document.getElementById("backlogList"),a=backlogColumnId();if(!n||!a)return;const s=backlogTasksNow();document.getElementById("backlogModalTitle").textContent=`${t("backlog.title")} (${s.length})`;const r=e.trim().toLowerCase(),o=r?s.filter(e=>[e.id,e.title,e.description,e.category,...e.tags].join(" ").toLowerCase().includes(r)):s;if(!o.length)return void(n.innerHTML=`<div class="empty-state">${escapeHtml(t("backlog.empty"))}</div>`);const i=!readOnly(),l=linkIndex(),c=i?config.columns.filter(e=>e.id!==a):[];n.innerHTML=o.map(e=>`\n <div class="archive-item${hkState.staleIds.has(e.id)?" hk-stale":""}" style="cursor: pointer;" data-id="${escapeHtml(e.id)}">\n <div class="top">\n <div>\n <span class="task-id">${escapeHtml(e.id)}</span>\n <strong style="margin-left: var(--s-2);">${escapeHtml(e.title)}</strong>\n </div>\n <div style="display: flex; gap: var(--s-2); flex-wrap: wrap; justify-content: flex-end;">\n ${c.map(e=>`<button data-action="promote" data-status="${escapeHtml(e.id)}" class="btn btn-secondary btn-sm" title="${escapeHtml(t("backlog.moveTo",{name:e.name}))}">&rarr; ${escapeHtml(e.name)}</button>`).join("")}\n ${i?`<button data-action="archive" class="btn btn-secondary btn-sm">${escapeHtml(t("backlog.archive"))}</button>`:""}\n </div>\n </div>\n ${e.description?`<div class="desc">${markdownToHtml(e.description)}</div>`:""}\n <div class="task-meta">\n ${blockedBadgeHtml(e,l)}\n ${e.priority?`<span class="badge">${priorityHtml(e.priority)}</span>`:""}\n ${e.category?`<span class="badge">${escapeHtml(e.category)}</span>`:""}\n ${e.tags.map(e=>`<span class="tag">${escapeHtml(e)}</span>`).join("")}\n ${hkState.staleIds.has(e.id)?`<span class="badge">${escapeHtml(t("backlog.old"))}</span>`:""}\n </div>\n </div>\n `).join("")}async function onBacklogListClick(e){const n=e.target.closest(".archive-item");if(!n)return;const a=n.dataset.id,s=e.target.closest("[data-action]"),r=s&&s.dataset.action;if("promote"===r){await moveTask(a,s.dataset.status,null,{askOrder:!1})&&showNotification(t("notif.taskMoved"),"success")}else if("archive"===r){const e=tasks.find(e=>e.id===a);if(!e||!confirm(t("confirm.archiveTask",{title:e.title})))return;await runOrderOp({type:"archiveTask",id:a},"notif.taskArchived")}else{const e=tasks.find(e=>e.id===a);e&&showTaskDetail(e)}}async function toggleBacklogFile(){if(readOnly())return;const e=resolveBacklogFile(config);if(e){if(!confirm(t("backlog.confirmOff",{count:backlogTasksNow().length,file:e.file})))return;return void await runOrderOp({type:"setBacklogFile",enabled:!1},"backlog.offDone")}let n=(resolveBacklogFile({columns:config.columns,backlogFile:{file:"backlog.md"}})||{}).column;if(!n&&(n=(prompt(t("backlog.askColumn",{ids:config.columns.map(e=>e.id).join(", ")}),"")||"").trim(),!n))return;const a=tasks.filter(e=>e.status===n).length;confirm(t("backlog.confirmOn",{count:a,column:n}))&&await runOrderOp({type:"setBacklogFile",enabled:!0,column:n},"backlog.onDone")}function updateBacklogMenu(){const e=document.getElementById("backlogToggleBtn"),n=document.getElementById("backlogOpenBtn");if(!e||!n)return;const a=boardLoaded(),s=a&&!!backlogColumnId();e.style.display=a&&!readOnly()?"inline-flex":"none",n.style.display=s?"inline-flex":"none",e.querySelector(".mi-label").textContent=t(s?"backlog.menuOn":"backlog.menuOff"),n.querySelector(".mi-label").textContent=t("backlog.openMenu")}document.getElementById("backlogSearch").addEventListener("input",e=>renderBacklogList(e.target.value));let shareState={task:null,format:"text",view:"raw",notes:!0,assignees:!0};function buildShareText(){const{task:e,format:t,notes:n,assignees:a}=shareState;return e?"markdown"===t?taskToMarkdown(e,{notes:n,assignees:a}):taskToPlainText(e,config.columns,{notes:n,assignees:a}):""}function openShareDialog(){currentDetailTask&&(shareState={task:currentDetailTask,format:shareState.format,view:shareState.view,notes:!0,assignees:!0},document.getElementById("shareModal").classList.add("active"),refreshShareDialog())}function closeShareDialog(){document.getElementById("shareModal").classList.remove("active")}function setShareOption(e,t){shareState[e]=t,refreshShareDialog()}function refreshShareDialog(){const{task:e,format:n,view:a,notes:s,assignees:r}=shareState;if(!e)return;document.getElementById("shareModalTitle").textContent=t("share.title"),document.getElementById("shareHint").textContent=t("share.hint"),document.getElementById("shareFormatText").classList.toggle("btn-primary","text"===n),document.getElementById("shareFormatText").classList.toggle("btn-secondary","text"!==n),document.getElementById("shareFormatMd").classList.toggle("btn-primary","markdown"===n),document.getElementById("shareFormatMd").classList.toggle("btn-secondary","markdown"!==n),document.getElementById("shareFormatText").textContent=t("share.text"),document.getElementById("shareFormatMd").textContent=t("share.markdown"),document.getElementById("shareNotesLabel").textContent=t("share.includeNotes"),document.getElementById("shareAssigneesLabel").textContent=t("share.includeAssignees"),document.getElementById("shareNotes").checked=s,document.getElementById("shareAssignees").checked=r,document.getElementById("shareNotesRow").style.display=e.notes||e.extra?"":"none",document.getElementById("shareAssigneesRow").style.display=e.assignees.length?"":"none";const o=buildShareText(),i=document.getElementById("sharePreview"),l=document.getElementById("shareRendered"),c="markdown"===n&&"rendered"===a;i.value=o,i.style.display=c?"none":"",l.style.display=c?"":"none",c&&(l.innerHTML=renderGfm(o)),document.getElementById("shareView").style.display="markdown"===n?"":"none";for(const[e,t]of[["shareViewRendered","rendered"===a],["shareViewRaw","raw"===a]]){const n=document.getElementById(e);n.classList.toggle("btn-primary",t),n.classList.toggle("btn-secondary",!t),n.setAttribute("aria-pressed",t)}document.getElementById("shareCopyBtn").textContent=t("share.copy");const d=document.getElementById("shareNativeBtn");d.textContent=t("share.share");const u="function"==typeof navigator.share;d.style.display=u?"":"none";const p=document.getElementById("shareCopyBtn");p.classList.toggle("btn-primary",!u),p.classList.toggle("btn-secondary",u),d.classList.add("btn-primary"),d.classList.remove("btn-secondary")}async function copyShare(){const e=document.getElementById("sharePreview").value;try{if(!navigator.clipboard||!navigator.clipboard.writeText)throw new Error("no clipboard API");await navigator.clipboard.writeText(e),showNotification(t("share.copied"),"success")}catch(n){const a=document.createElement("textarea");a.value=e,document.body.appendChild(a),a.select();let s=!1;try{s=document.execCommand("copy")}catch(e){}a.remove(),showNotification(t(s?"share.copied":"share.copyFailed"),s?"success":"error")}}async function nativeShare(){const{task:e}=shareState;try{await navigator.share({title:e.title,text:document.getElementById("sharePreview").value})}catch(e){e&&"AbortError"!==e.name&&showNotification(t("share.copyFailed"),"error")}}</script></body></html>