kandown 0.13.0 → 0.13.1

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.
Binary file
Binary file
Binary file
package/dist/favicon.ico CHANGED
Binary file
package/dist/favicon.svg CHANGED
@@ -1,25 +1,39 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <!--
3
3
  @file public/favicon.svg
4
- @description Kandown brand mark — vector favicon.
5
- 📖 Hand-traced from kandownlogo.png. Optimised for 16–256px rendering.
6
- Colors: dark surface #0a0a0a, K strokes #ffffff, lime accent #cef867.
7
- The lime slash is a parallelogram that slices from top-right to bottom-left,
8
- crossing the K. Z-order is: surface → K strokes → lime slash.
4
+ @description Kandown brand mark — adaptive favicon.
5
+ 📖 Transparent background so the icon adapts to the browser's tab-strip
6
+ color (no more black-on-black on dark themes). The K recolors itself
7
+ via @media (prefers-color-scheme: dark) so the contrast holds in both
8
+ light and dark browser themes:
9
+ • light browser: dark K (#0a0a0a) on white/cream tab → high contrast
10
+ • dark browser: white K (#ffffff) on dark tab → high contrast
11
+ The lime slash (#cef867) is the constant brand element on top.
12
+ 📖 PNG fallbacks (16/32/48/ico) ship in the light-mode variant (dark K on
13
+ transparent) for browsers that don't honor SVG media queries. PWA /
14
+ OS-launcher icons (apple-touch, android-chrome, mstile, og-image)
15
+ still use the dark surface — those have a different use case (solid
16
+ background required by the platform).
9
17
  -->
10
18
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-label="Kandown">
11
19
  <title>Kandown</title>
12
- <rect width="256" height="256" rx="48" ry="48" fill="#0a0a0a"/>
20
+ <style>
21
+ .k { fill: #0a0a0a; }
22
+ .slash { fill: #cef867; }
23
+ @media (prefers-color-scheme: dark) {
24
+ .k { fill: #ffffff; }
25
+ }
26
+ </style>
13
27
 
14
28
  <!-- K vertical bar (left stem) -->
15
- <rect x="50" y="36" width="34" height="184" rx="3" ry="3" fill="#ffffff"/>
29
+ <rect class="k" x="50" y="36" width="34" height="184" rx="3" ry="3"/>
16
30
 
17
31
  <!-- K upper diagonal: middle-left → top-right -->
18
- <path d="M 84 128 L 200 36 L 222 36 L 84 152 Z" fill="#ffffff"/>
32
+ <path class="k" d="M 84 128 L 200 36 L 222 36 L 84 152 Z"/>
19
33
 
20
34
  <!-- K lower diagonal: middle-left → bottom-right (with the small flag/notch on the right) -->
21
- <path d="M 84 128 L 200 220 L 222 220 L 188 186 L 200 174 L 84 132 Z" fill="#ffffff"/>
35
+ <path class="k" d="M 84 128 L 200 220 L 222 220 L 188 186 L 200 174 L 84 132 Z"/>
22
36
 
23
37
  <!-- Lime diagonal slash: thick parallelogram from top-right to bottom-left -->
24
- <path d="M 232 22 L 174 22 L 24 172 L 24 230 Z" fill="#cef867"/>
38
+ <path class="slash" d="M 232 22 L 174 22 L 24 172 L 24 230 Z"/>
25
39
  </svg>
package/dist/index.html CHANGED
@@ -113,7 +113,7 @@ Error generating stack: `+y.message+`
113
113
 
114
114
  ## Subtasks
115
115
 
116
- `}}async function joe(e,t){try{return await(await(await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}async function Doe(e,t){try{return await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`),!0}catch{return!1}}async function Nz(e,t){try{return await e.getDirectoryHandle("archive",{create:t})}catch{return null}}async function Ioe(e){if(Qt())return V4();const t=new Set;for await(const n of e.values())n.kind==="file"&&n.name.endsWith(".md")&&t.add(n.name.slice(0,-3));try{const n=await e.getDirectoryHandle("archive",{create:!1});for await(const a of n.values())a.kind==="file"&&a.name.endsWith(".md")&&t.add(a.name.slice(0,-3))}catch{}return[...t].sort((n,a)=>n.localeCompare(a,void 0,{numeric:!0}))}async function Wr(e,t){if(Qt())try{const i=await Cz(t);return o3(i)}catch{return oj(t)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}})(e)??await joe(e,t);return a!==null?o3(a):oj(t)}async function eu(e,t,n,a){const i=U4(n,a);if(Qt())return Noe(t,i);const o=await(await(await Doe(e,t)?await Nz(e,!0):e).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close()}async function cj(e,t){if(Qt())return Soe(t);try{await e.removeEntry(`${t}.md`)}catch{}try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}async function Roe(e,t){await bs(`/api/tasks/${encodeURIComponent(e)}/archive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function Ooe(e,t){await bs(`/api/tasks/${encodeURIComponent(e)}/unarchive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function Foe(e,t,n,a){const i=U4(n,a);if(Qt())return Roe(t,i);const o=await(await(await Nz(e,!0)).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close();try{await e.removeEntry(`${t}.md`)}catch{}}async function zoe(e,t,n,a){const i=U4(n,a);if(Qt())return Ooe(t,i);const s=await(await e.getFileHandle(`${t}.md`,{create:!0})).createWritable();await s.write(i),await s.close();try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}const Hoe="kanban-md",Boe=1,Mu="recentProjects";function Sz(){return new Promise((e,t)=>{const n=indexedDB.open(Hoe,Boe);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(Mu)||a.createObjectStore(Mu,{keyPath:"id"})}})}async function k2(e){const t=await Sz();return new Promise((n,a)=>{const i=t.transaction(Mu,"readwrite");i.objectStore(Mu).put(e),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function l3(){const e=await Sz();return new Promise((t,n)=>{const i=e.transaction(Mu,"readonly").objectStore(Mu).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),t(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function pj(e,t=!0){const n={mode:t?"readwrite":"read"};return await e.queryPermission(n)==="granted"||await e.requestPermission(n)==="granted"}function $z(e=Ci){const t=e.board.columns;return t[t.length-1]??"Done"}function qoe(e,t=Ci){return e===$z(t)||e.toLowerCase()==="archived"}class Uoe{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=150;start(t,n){this.dirHandle=t,this.tasksDirHandle=n,this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300)}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(t=>clearTimeout(t)),this.debounceTimers.clear()}on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await lj(this.dirHandle);t!==null&&(this.configHash=await this.hash(t)),await this.syncTaskDir(!1)}async tick(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await lj(this.dirHandle);if(t!==null){const n=await this.hash(t);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of this.knownTaskIds){const a=await dj(this.tasksDirHandle,n);if(a!==null){const i=await this.hash(a),r=this.taskHashes.get(n);r!==void 0&&i!==r&&(this.taskHashes.set(n,i),this.debouncedEmit("taskChanged",n))}}await this.syncTaskDir(!0)}debouncedEmit(t,...n){const a=t+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(t,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(t){if(this.tasksDirHandle){for await(const n of this.tasksDirHandle.values())if(n.kind==="file"&&n.name.endsWith(".md")){const a=n.name.replace(".md","");if(!this.knownTaskIds.has(a)){this.knownTaskIds.add(a);const i=await dj(this.tasksDirHandle,a);i!==null&&(this.taskHashes.set(a,await this.hash(i)),t&&this.debouncedEmit("newTaskDetected",a))}}}}async hash(t){const a=new TextEncoder().encode(t),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(t,...n){this.listeners.get(t)?.forEach(i=>{if(t==="configChanged"){i();return}if(t==="taskChanged"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function lj(e){try{return await(await(await e.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function dj(e,t){try{return await(await(await e.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}const tu=new Uoe,Voe={soft:[{frequency:520,durationMs:90,delayMs:0,type:"sine"},{frequency:720,durationMs:120,delayMs:95,type:"sine"}],chime:[{frequency:660,durationMs:120,delayMs:0,type:"triangle"},{frequency:880,durationMs:160,delayMs:125,type:"triangle"}],ping:[{frequency:920,durationMs:110,delayMs:0,type:"sine"}],pop:[{frequency:260,durationMs:55,delayMs:0,type:"square"},{frequency:520,durationMs:80,delayMs:60,type:"square"}]};let uj=null;function Ez(){return"Notification"in window?Notification.permission:"unsupported"}async function Zoe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function w2({title:e,body:t,config:n}){if(n.notifications.sound&&Goe(n.notifications.soundId),!(!n.notifications.browser||Ez()!=="granted"))try{new Notification(e,{body:t})}catch{}}function Goe(e){const t=Voe[e],n=window.AudioContext??window.webkitAudioContext;if(!(!n||t.length===0))try{uj??=new n;const a=uj;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;t.forEach(r=>{const s=a.createOscillator(),o=a.createGain(),c=i+r.delayMs/1e3,p=c+r.durationMs/1e3;s.type=r.type??"sine",s.frequency.setValueAtTime(r.frequency,c),o.gain.setValueAtTime(1e-4,c),o.gain.exponentialRampToValueAtTime(.08,c+.01),o.gain.exponentialRampToValueAtTime(1e-4,p),s.connect(o),o.connect(a.destination),s.start(c),s.stop(p+.02)})}catch{}}function Woe(e){let t=-1;for(const n of e)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>t&&(t=r)}}return"t"+(t+1)}async function Koe(){const e=await V4();return await Promise.all(e.map(async n=>{const{frontmatter:a,body:i}=await Az(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Ds(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function Yoe(e){const t=await Ioe(e);return await Promise.all(t.map(async a=>{const{frontmatter:i,body:r}=await Wr(e,a),s={...i,id:i.id||a,status:i.status||"Backlog"},{subtasks:o,bodyWithoutSubtasks:c}=Ds(r);return{id:a,frontmatter:s,body:c,subtasks:o}}))}async function mj(e,t,n){const a=[];for(const i of t){const r=i.name;i.tasks.forEach((s,o)=>{a.push((async()=>{const{frontmatter:c,body:p}=await Wr(e,s.id);await eu(e,s.id,{...c,id:s.id,status:r,order:o},p)})())})}await Promise.all(a)}function nl(e){woe(e.ui.theme,e.ui.skin,e.ui.font,e.ui.background)}function d3(e){return{title:e.frontmatter.title||e.id,status:e.frontmatter.status||"Backlog",body:e.body,subtasks:e.subtasks}}function x2(e){du.clear(),e.forEach(t=>{du.set(t.id,d3(t))})}function Xoe(e){const t=e.split(/[\\/]+/).filter(Boolean),n=t[t.length-1];return n===".kandown"?t[t.length-2]??"Project":n??"Project"}function Qoe(e,t){return t.reduce((n,a,i)=>{const r=e[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function Joe(e,t){const n=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return e.title!==t.title||e.body!==t.body||JSON.stringify(n)!==JSON.stringify(a)}let ece=0;const du=new Map,Ef=new Map;let A2=null;const fe=moe((e,t)=>({isOpen:!1,loading:!1,dirHandle:null,projectName:null,tasksDirHandle:null,boardTitle:"Project Kanban",columns:[],archivedTasks:[],showArchives:!1,showMetadata:!0,taskContents:new Map,searchMatches:new Map,viewMode:localStorage.getItem("kandown:view")||"board",density:localStorage.getItem("kandown:density")||"comfortable",filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},commandOpen:!1,cheatsheetOpen:!1,drawerTaskId:null,drawerData:null,currentPage:"board",config:Ci,recentProjects:[],toasts:[],agentHook:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{const n=await Toe();if(!n)return;const{projectHandle:a,kandownHandle:i,tasksHandle:r}=n,s=a.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`);const o=Qt()?_2():null;await k2({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}}),await t().loadConfig(),await t().reloadBoard();const c=await l3();e({recentProjects:c}),t().setupWatcher()},openRecentProject:async n=>{if(!await pj(n.handle,!0)){t().toast("Permission denied","error");return}const i=await c3(n.handle),r=await p3(n.handle),s=n.handle.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`),await k2({...n,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},openServerProject:async()=>{e({loading:!0});try{const n=_2();if(!n)throw new Error("No server root");await sj();const a=Xoe(n),i=await xz();nl(i);const r=await V4(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await Az(f),k={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:x}=Ds(b);return{id:f,frontmatter:k,body:x,subtasks:w}}));x2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:zd(f.body,f.subtasks)})),c=v2(o,i.board.columns),p=y2(o),d=c.reduce((f,h)=>f+h.tasks.length,0),m=new Map;if(d<=10)for(const f of s)m.set(f.frontmatter.id,{frontmatter:f.frontmatter,subtasks:f.subtasks,body:f.body});e({loading:!1,isOpen:!0,config:i,columns:c,archivedTasks:p,boardTitle:"Project Kanban",projectName:a,taskContents:m,searchMatches:new Map}),window.history.pushState({},"",`?p=${encodeURIComponent(a)}`),t().setupWatcher(),t().refreshAgentHook()}catch{e({loading:!1,isOpen:!1}),t().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!Qt())return;const n=_2();if(!n)return;await sj();const a=await l3(),i=a.find(p=>p.kandownDir===n);if(!i){await t().openServerProject();return}if(!await pj(i.handle,!0)){await t().openServerProject();return}const s=await c3(i.handle),o=await p3(i.handle),c=i.handle.name;e({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),window.history.pushState({},"",`?p=${encodeURIComponent(c)}`),await k2({...i,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=t();if(!(!n&&!Qt()))try{const a=await Loe(n);a?(e({config:a}),nl(a)):(e({config:Ci}),nl(Ci))}catch{e({config:Ci}),nl(Ci)}},updateConfig:async n=>{const{dirHandle:a,config:i}=t();if(!a&&!Qt())return;const r=n(i);e({config:r}),nl(r);try{await Moe(a,r)}catch(s){t().toast("Failed to save config: "+s.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=t();if(Qt())try{const i=await Koe();x2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=v2(r,a.board.columns),o=y2(r);e({boardTitle:"Project Kanban",columns:s,archivedTasks:o});const c=s.reduce((d,m)=>d+m.tasks.length,0),p=new Map;if(c<=10)for(const d of i)p.set(d.frontmatter.id,{frontmatter:d.frontmatter,subtasks:d.subtasks,body:d.body});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}else if(n)try{const i=await Yoe(n);x2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=v2(r,a.board.columns),o=y2(r);e({boardTitle:"Project Kanban",columns:s,archivedTasks:o});const c=s.reduce((d,m)=>d+m.tasks.length,0),p=new Map;if(c<=10)for(const d of i)p.set(d.frontmatter.id,{frontmatter:d.frontmatter,subtasks:d.subtasks,body:d.body});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o}=t(),c=Qt();if(!c&&!t().tasksDirHandle)return;const p=s.find(x=>x.name===a),d=s.find(x=>x.name===i);if(!p||!d)return;const m=p.tasks.findIndex(x=>x.id===n);if(m===-1)return;const f=p.tasks[m];if(!f)return;if(qoe(i,o)){const x=new Map,S=$z(o).toLowerCase();for(const P of s)for(const L of P.tasks){const j=L.frontmatter&&(L.frontmatter.archived===!0||L.frontmatter.archived==="true");x.set(L.id,{exists:!0,resolved:j||L.id===n||P.name.toLowerCase()===S})}const E=[];for(const P of f.dependsOn){if(typeof P!="string"||!P.trim()||P===n)continue;const L=x.get(P);(!L||!L.resolved)&&E.push(P)}if(E.length>0){const P=E.length===1?E[0]:`${E.slice(0,-1).join(", ")} and ${E[E.length-1]}`;t().toast(`Cannot move ${n} to ${i}: blocked by ${P}`,"error");return}}const h=s.map(x=>({...x,tasks:[...x.tasks]})),b=h.find(x=>x.name===a),k=h.find(x=>x.name===i),[w]=b.tasks.splice(m,1);/done|termin|closed|complet/i.test(i)?w.checked=!0:w.checked=!1,r!==void 0?k.tasks.splice(r,0,w):k.tasks.push(w),e({columns:h});try{const{tasksDirHandle:x}=t();if(!x&&!c)return;const S=a===i?h.filter(E=>E.name===i):h.filter(E=>E.name===a||E.name===i);await mj(x??null,S,o.board.columns)}catch(x){t().toast("Failed to save: "+x.message,"error"),e({columns:s})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o}=t();if(!s&&!Qt())return;const c=r.map(m=>({...m,tasks:[...m.tasks]})),p=c.find(m=>m.name===n);if(!p)return;const[d]=p.tasks.splice(a,1);p.tasks.splice(i,0,d),e({columns:c});try{const{tasksDirHandle:m}=t(),f=Qt();if(!m&&!f)return;await mj(m??null,[p],o.board.columns)}catch(m){t().toast("Failed to save: "+m.message,"error"),e({columns:r})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=t();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await t().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await t().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=t();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(d=>d.name.toLowerCase()===i.toLowerCase())){t().toast("Column already exists","error");return}const c=r,p=r.map(d=>d.name===n?{...d,name:i}:d);e({columns:p});try{const d=c.find(m=>m.name===n);d&&await Promise.all(d.tasks.map(async(m,f)=>{const{frontmatter:h,body:b}=await Wr(s,m.id);await eu(s,m.id,{...h,id:m.id,status:i,order:f},b)})),await t().updateConfig(m=>{const f={...m.board.columnColors??{}},h=f[n.toLowerCase()];h&&(f[i.toLowerCase()]=h,delete f[n.toLowerCase()]);const b=m.board.columns.some(k=>k.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:b.map(k=>k.toLowerCase()===n.toLowerCase()?i:k),columnColors:f}}}),await t().reloadBoard()}catch(d){t().toast("Failed to rename column: "+d.message,"error"),e({columns:c})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=t();if(!i&&!Qt())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;e({columns:a.filter(o=>o.name!==n)});try{await Promise.all(r.tasks.map(o=>cj(i,o.id))),await t().updateConfig(o=>{const c={...o.board.columnColors??{}};return delete c[n.toLowerCase()],{...o,board:{...o.board,columns:o.board.columns.filter(p=>p.toLowerCase()!==n.toLowerCase()),columnColors:c}}}),await t().reloadBoard(),t().toast("Column deleted")}catch(o){t().toast("Failed to delete column: "+o.message,"error"),e({columns:s})}},createTask:async n=>{const{columns:a,tasksDirHandle:i,config:r,taskContents:s}=t();if(!i&&!Qt()||!a.length)return null;const o=n||r.board.columns[0]||a[0].name,c=Woe(a),p=a.find(f=>f.name===o)?.tasks.length??0,d={id:c,title:"",checked:!1,dependsOn:[],tags:[],assignee:null,priority:r.fields.priority?r.board.defaultPriority:null,ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",progress:null,frontmatter:{}},m=a.map(f=>f.name===o?{...f,tasks:[...f.tasks,d]}:f);e({columns:m});try{const f={id:c,title:"",status:o,order:p,priority:r.fields.priority?r.board.defaultPriority:"",tags:[],assignee:"",created:new Date().toISOString().slice(0,10),ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",tools:""},h="";await eu(i||null,c,f,h);const k=new Map(s);return k.set(c,{frontmatter:f,subtasks:[],body:h}),e({taskContents:k}),t().toast(`Created ${c.replace(/^t/,"")}`),await t().openDrawer(c),c}catch(f){return t().toast("Failed to create: "+f.message,"error"),e({columns:a}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r}=t();if(!i&&!Qt())return;const s=a.map(p=>({...p,tasks:p.tasks.filter(d=>d.id!==n)}));e({columns:s});const o=new Map(r);o.delete(n);const c=new Map(t().searchMatches);c.delete(n),e({taskContents:o,searchMatches:c});try{await cj(i||null,n),t().toast("Deleted")}catch(p){t().toast("Failed to delete: "+p.message,"error"),e({columns:a})}},archiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!Qt()))try{const{frontmatter:i,body:r}=await Wr(a||null,n);await Foe(a||null,n,{...i,archived:!0},r),t().drawerTaskId===n&&t().closeDrawer(),await t().reloadBoard(),t().toast("Archived")}catch(i){t().toast("Failed to archive: "+i.message,"error")}},unarchiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!Qt()))try{const{frontmatter:i,body:r}=await Wr(a||null,n),s={...i};delete s.archived,await zoe(a||null,n,s,r),await t().reloadBoard(),t().toast("Restored")}catch(i){t().toast("Failed to restore: "+i.message,"error")}},setShowArchives:n=>e({showArchives:n}),setShowMetadata:n=>e({showMetadata:n}),openDrawer:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!Qt()))try{const{frontmatter:i,body:r}=await Wr(a,n),{subtasks:s,bodyWithoutSubtasks:o}=Ds(r),c={frontmatter:i,subtasks:s,body:o,savedAt:Date.now()};e({drawerTaskId:n,drawerData:{frontmatter:i,subtasks:s,body:o},drawerBaseVersion:c,conflictState:null,showConflictModal:!1})}catch(i){t().toast("Failed to open: "+i.message,"error")}},closeDrawer:()=>e({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1}),updateDrawerData:n=>{const{drawerData:a}=t();a&&e({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!n||!a)return;const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await eu(i||null,n,o,s),t().toast("Saved"),e({drawerTaskId:null,drawerData:null});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(c){t().toast("Failed to save: "+c.message,"error")}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!(!n||!a))try{const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};await eu(i||null,n,o,s);const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(s){t().toast("Failed to save: "+s.message,"error")}},setViewMode:n=>{localStorage.setItem("kandown:view",n),e({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),e({density:n})},setFilter:(n,a)=>{if(e(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=t(),o=a,c=i.flatMap(p=>p.tasks.map(d=>d.id));if(r){const p=c.filter(d=>!s.has(d));p.length>0?t().loadTaskContents(p).then(()=>{t().computeSearchMatches(o)}):t().computeSearchMatches(o)}}},clearFilters:()=>e({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>e({commandOpen:n}),setCheatsheetOpen:n=>e({cheatsheetOpen:n}),refreshAgentHook:async()=>{if(!Qt()){e({agentHook:null});return}const n=await $oe();e({agentHook:n?.agentHook??null})},sendTaskToAgent:async n=>{const a=t().agentHook;if(!a){t().toast("Agent hook not configured","error");return}t().toast(`Sending to ${a.label}…`);const i=await Eoe(n);if(i===null){t().toast("Could not reach the daemon","error");return}i.ok?t().toast(`Sent to ${a.label}`):t().toast(i.error||"Agent hook failed","error")},setCurrentPage:n=>e({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=t();if(!a)return;const i=new Map(t().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await Wr(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Ds(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),e({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){e({searchMatches:new Map});return}const{taskContents:a}=t(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=wz(o,r);c.length>0&&i.set(s,c)}e({searchMatches:i})},toast:(n,a="success")=>{const i=++ece;e(r=>({toasts:[...r.toasts,{id:i,message:n,type:a}]})),setTimeout(()=>{e(r=>({toasts:r.toasts.filter(s=>s.id!==i)}))},2500)},dismissToast:n=>e(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=t();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await Wr(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Ds(p);e({drawerData:{frontmatter:c,subtasks:d,body:m},drawerBaseVersion:{frontmatter:c,subtasks:d,body:m,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=zd(i.body,i.subtasks),p={...i.frontmatter,id:s};await eu(r,s,p,c),e({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Overwritten remote changes")}}else e({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{if(Qt()){A2&&clearInterval(A2),A2=setInterval(()=>{t().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=t();if(!n||!a)return;tu.stop(),Ef.forEach(s=>clearTimeout(s)),Ef.clear(),tu.start(n,a);const i=(s,o)=>{const c=Ef.get(s);c&&clearTimeout(c);const p=Math.max(2e3,t().config.notifications.editDebounceMs),d=setTimeout(()=>{Ef.delete(s);const m=t().config;m.notifications.taskEdits&&w2({title:"Task edited",body:`${o} changed on disk.`,config:m})},p);Ef.set(s,d)},r=async s=>{const{tasksDirHandle:o,config:c}=t();if(!o)return;const{frontmatter:p,body:d}=await Wr(o,s),{subtasks:m,bodyWithoutSubtasks:f}=Ds(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},b=d3(h),k=du.get(s);if(!k){du.set(s,b);return}c.notifications.statusChanges&&k.status!==b.status&&w2({title:"Task status changed",body:`${b.title}: ${k.status} → ${b.status}`,config:c});const w=Qoe(k.subtasks,b.subtasks);c.notifications.subtaskCompletions&&w>0&&w2({title:"Subtask completed",body:w===1?`${b.title}: 1 subtask completed.`:`${b.title}: ${w} subtasks completed.`,config:c}),Joe(k,b)&&i(s,b.title),du.set(s,b)};tu.on("configChanged",()=>{t().loadConfig(),t().toast("Settings updated externally","info")}),tu.on("taskChanged",async s=>{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=t();if(await r(s),o===s&&c&&p){const{frontmatter:d,body:m}=await Wr(p,s),{subtasks:f,bodyWithoutSubtasks:h}=Ds(m),b=c,k=JSON.stringify(b.frontmatter)!==JSON.stringify(d),w=b.body!==h,x=JSON.stringify(b.subtasks)!==JSON.stringify(f);if(!k&&!w&&!x)return;let S="none";k&&(w||x)?S="full":k?S="metadata-only":(w||x)&&(S="body-only"),e({conflictState:{taskId:s,type:S,local:b,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:S==="full"})}else t().reloadBoard()}),tu.on("newTaskDetected",async s=>{const{tasksDirHandle:o}=t();if(o){const{frontmatter:c,body:p}=await Wr(o,s),{subtasks:d,bodyWithoutSubtasks:m}=Ds(p);du.set(s,d3({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:m,subtasks:d}))}t().reloadBoard()})}}));l3().then(e=>{fe.setState({recentProjects:e})});nl(Ci);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{nl(fe.getState().config)});function tce(){const e=fe(o=>o.config),t=fe(o=>o.updateConfig),[n,a]=$.useState(!1);if($.useEffect(()=>{a(!0)},[]),!n)return null;const i=e.ui.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.ui.theme,r=i==="dark",s=()=>{const o=r?"light":"dark";t(c=>({...c,ui:{...c.ui,theme:o}}))};return _.jsx(_t.button,{onClick:s,className:r3("relative inline-flex items-center rounded-full transition-all duration-300","border border-black/[0.08] dark:border-white/[0.12]","bg-black/[0.05] dark:bg-white/[0.08]","h-8 w-[52px] px-1"),title:r?"Switch to light mode":"Switch to dark mode",children:_.jsx(_t.div,{className:r3("inline-flex items-center justify-center rounded-full shadow-md",r?"bg-[#1e293b] border border-white/[0.15]":"bg-black border border-black/20","h-[26px] w-[26px]"),animate:{x:r?22:0},transition:{type:"spring",stiffness:500,damping:30},children:_.jsx(_t.div,{initial:{rotate:-90,scale:.5,opacity:0},animate:{rotate:0,scale:1,opacity:1},transition:{duration:.25,ease:"easeOut"},children:r?_.jsx(Zn.Moon,{size:13,className:"text-sky-300"}):_.jsx(Zn.Sun,{size:13,className:"text-amber-400"})},i)})})}function nce(e){const t=cse(e,{stiffness:180,damping:22,mass:.8});return $.useEffect(()=>{t.set(e)},[e,t]),nz(t,a=>Math.round(a).toString())}const u3="0.13.0",ace=({className:e})=>_.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 150 150",className:e,fill:"currentColor",children:[_.jsx("path",{d:"m57.6 64.6 0.1-0.1v-31.3c-0.1-3.5-2.7-5.6-5.7-5.6h-16.3v59.6c0.2-1.3 0.9-2.6 1.7-3.2l20.2-19.4z"}),_.jsx("path",{d:"m87.6 43.8c-3.4 0.1-7.1 1.3-9.9 3.7l-38.5 38.7c-2.1 2.1-3.4 4.8-3.5 7.5v26.7l77.5-76.4v-0.2h-25.6z"}),_.jsx("path",{d:"m108.1 96.4-6 5-22.4-20.8-14.6 14.2 21.1 21.4-5 4.1c-0.6 0.5-0.3 0.9 0.2 0.9h27.6c0.4 0 0.7-0.4 0.7-0.6v-24.8c0-0.7-1.1-0.3-1.6 0.3v0.3z"})]});function ice(){const{t:e}=Rn(),t=fe(V=>V.dirHandle),n=fe(V=>V.isOpen);fe(V=>V.projectName);const a=fe(V=>V.archivedTasks.length),i=fe(V=>V.showArchives),r=fe(V=>V.setShowArchives),s=fe(V=>V.columns),o=fe(V=>V.openFolder),c=fe(V=>V.reloadBoard),p=fe(V=>V.createTask),d=fe(V=>V.setCommandOpen),m=fe(V=>V.viewMode),f=fe(V=>V.setViewMode),h=fe(V=>V.density),b=fe(V=>V.setDensity),k=fe(V=>V.setCurrentPage),w=fe(V=>V.recentProjects),x=fe(V=>V.openRecentProject),S=fe(V=>V.filters),E=fe(V=>V.setFilter),P=fe(V=>V.clearFilters),L=fe(V=>V.config.fields),[j,F]=$.useState(!1),z=$.useRef(null),R=$.useRef(null),M=s.reduce((V,H)=>V+H.tasks.length,0),K=nce(M),J=[];L.priority&&S.priority&&J.push({type:"priority",label:S.priority,value:S.priority}),L.tags&&S.tag&&J.push({type:"tag",label:"#"+S.tag,value:S.tag}),L.assignee&&S.assignee&&J.push({type:"assignee",label:"@"+S.assignee,value:S.assignee});const W=[{label:e("filterBar.ownerAll"),value:""},{label:e("filterBar.ownerHuman"),value:"human"},{label:e("filterBar.ownerAI"),value:"ai"}],B=J.length>0||S.search||L.ownerType&&S.ownerType;return $.useEffect(()=>{if(!j)return;const V=H=>{z.current&&!z.current.contains(H.target)&&F(!1)};return document.addEventListener("mousedown",V),()=>document.removeEventListener("mousedown",V)},[j]),_.jsxs("header",{className:"flex items-center justify-between px-5 h-[64px] border-b border-border bg-card/80 backdrop-blur-xl relative z-10",children:[_.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[_.jsx("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:_.jsxs("button",{onClick:()=>window.history.pushState({},"",window.location.pathname),className:"flex items-center gap-2 cursor-pointer",children:[_.jsx(ace,{className:"w-[34px] h-[34px] dark:text-white text-black"}),_.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),_.jsxs("span",{className:"inline-flex items-center h-5 px-1.5 text-[10.5px] font-semibold text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-500/15 rounded-md",children:["v",u3]})]})}),t&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.jsxs("div",{className:"flex items-center gap-2 px-3 h-9 bg-secondary/60 border border-border rounded-xl min-w-[200px] max-w-[280px] focus-within:border-border-focus focus-within:bg-secondary transition-all",children:[_.jsx(Zn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),_.jsx("input",{ref:R,type:"text",placeholder:e("filterBar.searchPlaceholder"),value:S.search,onChange:V=>E("search",V.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),S.search?_.jsx("button",{onClick:()=>E("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:_.jsx(Zn.X,{size:14})}):_.jsx("kbd",{className:"inline-flex items-center h-5 px-1.5 text-[10px] font-medium text-fg-muted/50 bg-black/[0.04] dark:bg-white/[0.08] rounded border border-black/[0.06] dark:border-white/[0.1]",children:"⌘K"})]}),_.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[_.jsx(ps,{children:J.map(V=>_.jsxs(_t.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>E(V.type,null),className:"inline-flex items-center gap-1 h-6 px-2.5 text-[12px] text-fg bg-black/[0.05] dark:bg-white/[0.1] border border-black/[0.08] dark:border-white/[0.12] rounded-lg hover:bg-black/[0.08] dark:hover:bg-white/[0.15] transition-colors",children:[V.label,_.jsx(Zn.X,{size:10,className:"text-fg-muted/60"})]},V.type+V.value))}),L.ownerType&&_.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:W.map(V=>_.jsx("button",{onClick:()=>E("ownerType",V.value),className:`h-full px-2.5 text-[12px] transition-colors ${S.ownerType===V.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:V.label},V.value))}),B&&_.jsx("button",{onClick:P,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:e("filterBar.clearAll")})]})]}),t&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.jsxs("div",{className:"relative flex-shrink-0",ref:z,children:[_.jsxs("button",{onClick:()=>F(V=>!V),className:"flex items-center gap-1.5 px-2.5 py-1.5 text-[13px] text-fg-muted hover:text-fg hover:bg-black/[0.04] dark:hover:bg-white/[0.06] rounded-lg transition-colors border border-transparent hover:border-black/[0.06] dark:hover:border-white/[0.1]",children:[_.jsx(Zn.Folder,{size:13,className:"text-fg-muted/70"}),_.jsxs("span",{className:"font-medium",children:[".",t.name]}),_.jsx(Zn.ChevronDown,{size:11,className:"opacity-50"})]}),_.jsx(ps,{children:j&&_.jsx(_t.div,{initial:{opacity:0,y:-4,scale:.98},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:-4,scale:.98},transition:{duration:.12},className:"absolute top-full left-0 mt-2 min-w-[240px] glass rounded-xl shadow-[0_16px_48px_rgba(0,0,0,0.5)] overflow-hidden z-50",children:_.jsxs("div",{className:"py-1.5",children:[w.length>0&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:e("header.recentProjects")}),w.map(V=>_.jsxs("button",{onClick:()=>{F(!1),x(V)},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[_.jsx(Zn.Folder,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{className:"truncate",children:V.name}),V.id===t.name&&_.jsx(Zn.Check,{size:12,className:"ml-auto text-emerald-500"})]},V.id)),_.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),_.jsxs("button",{onClick:()=>{F(!1),o()},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[_.jsx(Zn.Plus,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{children:e("header.openFolder...")})]})]})})})]})]})]}),_.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||t?_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[_.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),_.jsx(_t.span,{className:"tabular-nums font-medium",children:K}),_.jsx("span",{children:e("header.tasks")})]}),_.jsxs("div",{className:"flex items-center bg-black/[0.04] dark:bg-white/[0.06] border border-black/[0.06] dark:border-white/[0.1] rounded-xl p-0.5 h-10",children:[_.jsx("button",{onClick:()=>f("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.board"),children:_.jsx(Zn.LayoutBoard,{size:18})}),_.jsx("button",{onClick:()=>f("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.list"),children:_.jsx(Zn.LayoutList,{size:18})})]}),_.jsx(da,{variant:"icon",icon:"Archive",onClick:()=>r(!i),title:i?e("header.backToBoard"):`${e("header.archives")} (${a})`,className:i?"text-accent":""}),_.jsx(da,{variant:"icon",icon:"Density",onClick:()=>b(h==="compact"?"comfortable":"compact"),title:`Density: ${h}`}),_.jsx(da,{variant:"icon",icon:"Settings",onClick:()=>k("settings"),title:e("common.settings")}),_.jsx(tce,{}),_.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),_.jsx(da,{variant:"secondary",icon:"Search",label:e("common.search"),shortcut:"⌘K",onClick:()=>d(!0),title:"Command palette (⌘K)"}),_.jsx(da,{variant:"icon",icon:"Refresh",onClick:c,title:`${e("common.reload")} (R)`}),_.jsx(da,{variant:"primary",icon:"Plus",label:e("common.newTask"),shortcut:"N",onClick:()=>p()})]}):_.jsx(da,{variant:"primary",label:e("common.openFolder"),onClick:o})})]})}/**
116
+ `}}async function joe(e,t){try{return await(await(await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}async function Doe(e,t){try{return await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`),!0}catch{return!1}}async function Nz(e,t){try{return await e.getDirectoryHandle("archive",{create:t})}catch{return null}}async function Ioe(e){if(Qt())return V4();const t=new Set;for await(const n of e.values())n.kind==="file"&&n.name.endsWith(".md")&&t.add(n.name.slice(0,-3));try{const n=await e.getDirectoryHandle("archive",{create:!1});for await(const a of n.values())a.kind==="file"&&a.name.endsWith(".md")&&t.add(a.name.slice(0,-3))}catch{}return[...t].sort((n,a)=>n.localeCompare(a,void 0,{numeric:!0}))}async function Wr(e,t){if(Qt())try{const i=await Cz(t);return o3(i)}catch{return oj(t)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}})(e)??await joe(e,t);return a!==null?o3(a):oj(t)}async function eu(e,t,n,a){const i=U4(n,a);if(Qt())return Noe(t,i);const o=await(await(await Doe(e,t)?await Nz(e,!0):e).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close()}async function cj(e,t){if(Qt())return Soe(t);try{await e.removeEntry(`${t}.md`)}catch{}try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}async function Roe(e,t){await bs(`/api/tasks/${encodeURIComponent(e)}/archive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function Ooe(e,t){await bs(`/api/tasks/${encodeURIComponent(e)}/unarchive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function Foe(e,t,n,a){const i=U4(n,a);if(Qt())return Roe(t,i);const o=await(await(await Nz(e,!0)).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close();try{await e.removeEntry(`${t}.md`)}catch{}}async function zoe(e,t,n,a){const i=U4(n,a);if(Qt())return Ooe(t,i);const s=await(await e.getFileHandle(`${t}.md`,{create:!0})).createWritable();await s.write(i),await s.close();try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}const Hoe="kanban-md",Boe=1,Mu="recentProjects";function Sz(){return new Promise((e,t)=>{const n=indexedDB.open(Hoe,Boe);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(Mu)||a.createObjectStore(Mu,{keyPath:"id"})}})}async function k2(e){const t=await Sz();return new Promise((n,a)=>{const i=t.transaction(Mu,"readwrite");i.objectStore(Mu).put(e),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function l3(){const e=await Sz();return new Promise((t,n)=>{const i=e.transaction(Mu,"readonly").objectStore(Mu).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),t(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function pj(e,t=!0){const n={mode:t?"readwrite":"read"};return await e.queryPermission(n)==="granted"||await e.requestPermission(n)==="granted"}function $z(e=Ci){const t=e.board.columns;return t[t.length-1]??"Done"}function qoe(e,t=Ci){return e===$z(t)||e.toLowerCase()==="archived"}class Uoe{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=150;start(t,n){this.dirHandle=t,this.tasksDirHandle=n,this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300)}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(t=>clearTimeout(t)),this.debounceTimers.clear()}on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await lj(this.dirHandle);t!==null&&(this.configHash=await this.hash(t)),await this.syncTaskDir(!1)}async tick(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await lj(this.dirHandle);if(t!==null){const n=await this.hash(t);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of this.knownTaskIds){const a=await dj(this.tasksDirHandle,n);if(a!==null){const i=await this.hash(a),r=this.taskHashes.get(n);r!==void 0&&i!==r&&(this.taskHashes.set(n,i),this.debouncedEmit("taskChanged",n))}}await this.syncTaskDir(!0)}debouncedEmit(t,...n){const a=t+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(t,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(t){if(this.tasksDirHandle){for await(const n of this.tasksDirHandle.values())if(n.kind==="file"&&n.name.endsWith(".md")){const a=n.name.replace(".md","");if(!this.knownTaskIds.has(a)){this.knownTaskIds.add(a);const i=await dj(this.tasksDirHandle,a);i!==null&&(this.taskHashes.set(a,await this.hash(i)),t&&this.debouncedEmit("newTaskDetected",a))}}}}async hash(t){const a=new TextEncoder().encode(t),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(t,...n){this.listeners.get(t)?.forEach(i=>{if(t==="configChanged"){i();return}if(t==="taskChanged"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function lj(e){try{return await(await(await e.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function dj(e,t){try{return await(await(await e.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}const tu=new Uoe,Voe={soft:[{frequency:520,durationMs:90,delayMs:0,type:"sine"},{frequency:720,durationMs:120,delayMs:95,type:"sine"}],chime:[{frequency:660,durationMs:120,delayMs:0,type:"triangle"},{frequency:880,durationMs:160,delayMs:125,type:"triangle"}],ping:[{frequency:920,durationMs:110,delayMs:0,type:"sine"}],pop:[{frequency:260,durationMs:55,delayMs:0,type:"square"},{frequency:520,durationMs:80,delayMs:60,type:"square"}]};let uj=null;function Ez(){return"Notification"in window?Notification.permission:"unsupported"}async function Zoe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function w2({title:e,body:t,config:n}){if(n.notifications.sound&&Goe(n.notifications.soundId),!(!n.notifications.browser||Ez()!=="granted"))try{new Notification(e,{body:t})}catch{}}function Goe(e){const t=Voe[e],n=window.AudioContext??window.webkitAudioContext;if(!(!n||t.length===0))try{uj??=new n;const a=uj;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;t.forEach(r=>{const s=a.createOscillator(),o=a.createGain(),c=i+r.delayMs/1e3,p=c+r.durationMs/1e3;s.type=r.type??"sine",s.frequency.setValueAtTime(r.frequency,c),o.gain.setValueAtTime(1e-4,c),o.gain.exponentialRampToValueAtTime(.08,c+.01),o.gain.exponentialRampToValueAtTime(1e-4,p),s.connect(o),o.connect(a.destination),s.start(c),s.stop(p+.02)})}catch{}}function Woe(e){let t=-1;for(const n of e)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>t&&(t=r)}}return"t"+(t+1)}async function Koe(){const e=await V4();return await Promise.all(e.map(async n=>{const{frontmatter:a,body:i}=await Az(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Ds(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function Yoe(e){const t=await Ioe(e);return await Promise.all(t.map(async a=>{const{frontmatter:i,body:r}=await Wr(e,a),s={...i,id:i.id||a,status:i.status||"Backlog"},{subtasks:o,bodyWithoutSubtasks:c}=Ds(r);return{id:a,frontmatter:s,body:c,subtasks:o}}))}async function mj(e,t,n){const a=[];for(const i of t){const r=i.name;i.tasks.forEach((s,o)=>{a.push((async()=>{const{frontmatter:c,body:p}=await Wr(e,s.id);await eu(e,s.id,{...c,id:s.id,status:r,order:o},p)})())})}await Promise.all(a)}function nl(e){woe(e.ui.theme,e.ui.skin,e.ui.font,e.ui.background)}function d3(e){return{title:e.frontmatter.title||e.id,status:e.frontmatter.status||"Backlog",body:e.body,subtasks:e.subtasks}}function x2(e){du.clear(),e.forEach(t=>{du.set(t.id,d3(t))})}function Xoe(e){const t=e.split(/[\\/]+/).filter(Boolean),n=t[t.length-1];return n===".kandown"?t[t.length-2]??"Project":n??"Project"}function Qoe(e,t){return t.reduce((n,a,i)=>{const r=e[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function Joe(e,t){const n=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return e.title!==t.title||e.body!==t.body||JSON.stringify(n)!==JSON.stringify(a)}let ece=0;const du=new Map,Ef=new Map;let A2=null;const fe=moe((e,t)=>({isOpen:!1,loading:!1,dirHandle:null,projectName:null,tasksDirHandle:null,boardTitle:"Project Kanban",columns:[],archivedTasks:[],showArchives:!1,showMetadata:!0,taskContents:new Map,searchMatches:new Map,viewMode:localStorage.getItem("kandown:view")||"board",density:localStorage.getItem("kandown:density")||"comfortable",filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},commandOpen:!1,cheatsheetOpen:!1,drawerTaskId:null,drawerData:null,currentPage:"board",config:Ci,recentProjects:[],toasts:[],agentHook:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{const n=await Toe();if(!n)return;const{projectHandle:a,kandownHandle:i,tasksHandle:r}=n,s=a.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`);const o=Qt()?_2():null;await k2({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}}),await t().loadConfig(),await t().reloadBoard();const c=await l3();e({recentProjects:c}),t().setupWatcher()},openRecentProject:async n=>{if(!await pj(n.handle,!0)){t().toast("Permission denied","error");return}const i=await c3(n.handle),r=await p3(n.handle),s=n.handle.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`),await k2({...n,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},openServerProject:async()=>{e({loading:!0});try{const n=_2();if(!n)throw new Error("No server root");await sj();const a=Xoe(n),i=await xz();nl(i);const r=await V4(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await Az(f),k={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:x}=Ds(b);return{id:f,frontmatter:k,body:x,subtasks:w}}));x2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:zd(f.body,f.subtasks)})),c=v2(o,i.board.columns),p=y2(o),d=c.reduce((f,h)=>f+h.tasks.length,0),m=new Map;if(d<=10)for(const f of s)m.set(f.frontmatter.id,{frontmatter:f.frontmatter,subtasks:f.subtasks,body:f.body});e({loading:!1,isOpen:!0,config:i,columns:c,archivedTasks:p,boardTitle:"Project Kanban",projectName:a,taskContents:m,searchMatches:new Map}),window.history.pushState({},"",`?p=${encodeURIComponent(a)}`),t().setupWatcher(),t().refreshAgentHook()}catch{e({loading:!1,isOpen:!1}),t().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!Qt())return;const n=_2();if(!n)return;await sj();const a=await l3(),i=a.find(p=>p.kandownDir===n);if(!i){await t().openServerProject();return}if(!await pj(i.handle,!0)){await t().openServerProject();return}const s=await c3(i.handle),o=await p3(i.handle),c=i.handle.name;e({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),window.history.pushState({},"",`?p=${encodeURIComponent(c)}`),await k2({...i,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=t();if(!(!n&&!Qt()))try{const a=await Loe(n);a?(e({config:a}),nl(a)):(e({config:Ci}),nl(Ci))}catch{e({config:Ci}),nl(Ci)}},updateConfig:async n=>{const{dirHandle:a,config:i}=t();if(!a&&!Qt())return;const r=n(i);e({config:r}),nl(r);try{await Moe(a,r)}catch(s){t().toast("Failed to save config: "+s.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=t();if(Qt())try{const i=await Koe();x2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=v2(r,a.board.columns),o=y2(r);e({boardTitle:"Project Kanban",columns:s,archivedTasks:o});const c=s.reduce((d,m)=>d+m.tasks.length,0),p=new Map;if(c<=10)for(const d of i)p.set(d.frontmatter.id,{frontmatter:d.frontmatter,subtasks:d.subtasks,body:d.body});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}else if(n)try{const i=await Yoe(n);x2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=v2(r,a.board.columns),o=y2(r);e({boardTitle:"Project Kanban",columns:s,archivedTasks:o});const c=s.reduce((d,m)=>d+m.tasks.length,0),p=new Map;if(c<=10)for(const d of i)p.set(d.frontmatter.id,{frontmatter:d.frontmatter,subtasks:d.subtasks,body:d.body});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o}=t(),c=Qt();if(!c&&!t().tasksDirHandle)return;const p=s.find(x=>x.name===a),d=s.find(x=>x.name===i);if(!p||!d)return;const m=p.tasks.findIndex(x=>x.id===n);if(m===-1)return;const f=p.tasks[m];if(!f)return;if(qoe(i,o)){const x=new Map,S=$z(o).toLowerCase();for(const P of s)for(const L of P.tasks){const j=L.frontmatter&&(L.frontmatter.archived===!0||L.frontmatter.archived==="true");x.set(L.id,{exists:!0,resolved:j||L.id===n||P.name.toLowerCase()===S})}const E=[];for(const P of f.dependsOn){if(typeof P!="string"||!P.trim()||P===n)continue;const L=x.get(P);(!L||!L.resolved)&&E.push(P)}if(E.length>0){const P=E.length===1?E[0]:`${E.slice(0,-1).join(", ")} and ${E[E.length-1]}`;t().toast(`Cannot move ${n} to ${i}: blocked by ${P}`,"error");return}}const h=s.map(x=>({...x,tasks:[...x.tasks]})),b=h.find(x=>x.name===a),k=h.find(x=>x.name===i),[w]=b.tasks.splice(m,1);/done|termin|closed|complet/i.test(i)?w.checked=!0:w.checked=!1,r!==void 0?k.tasks.splice(r,0,w):k.tasks.push(w),e({columns:h});try{const{tasksDirHandle:x}=t();if(!x&&!c)return;const S=a===i?h.filter(E=>E.name===i):h.filter(E=>E.name===a||E.name===i);await mj(x??null,S,o.board.columns)}catch(x){t().toast("Failed to save: "+x.message,"error"),e({columns:s})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o}=t();if(!s&&!Qt())return;const c=r.map(m=>({...m,tasks:[...m.tasks]})),p=c.find(m=>m.name===n);if(!p)return;const[d]=p.tasks.splice(a,1);p.tasks.splice(i,0,d),e({columns:c});try{const{tasksDirHandle:m}=t(),f=Qt();if(!m&&!f)return;await mj(m??null,[p],o.board.columns)}catch(m){t().toast("Failed to save: "+m.message,"error"),e({columns:r})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=t();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await t().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await t().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=t();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(d=>d.name.toLowerCase()===i.toLowerCase())){t().toast("Column already exists","error");return}const c=r,p=r.map(d=>d.name===n?{...d,name:i}:d);e({columns:p});try{const d=c.find(m=>m.name===n);d&&await Promise.all(d.tasks.map(async(m,f)=>{const{frontmatter:h,body:b}=await Wr(s,m.id);await eu(s,m.id,{...h,id:m.id,status:i,order:f},b)})),await t().updateConfig(m=>{const f={...m.board.columnColors??{}},h=f[n.toLowerCase()];h&&(f[i.toLowerCase()]=h,delete f[n.toLowerCase()]);const b=m.board.columns.some(k=>k.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:b.map(k=>k.toLowerCase()===n.toLowerCase()?i:k),columnColors:f}}}),await t().reloadBoard()}catch(d){t().toast("Failed to rename column: "+d.message,"error"),e({columns:c})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=t();if(!i&&!Qt())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;e({columns:a.filter(o=>o.name!==n)});try{await Promise.all(r.tasks.map(o=>cj(i,o.id))),await t().updateConfig(o=>{const c={...o.board.columnColors??{}};return delete c[n.toLowerCase()],{...o,board:{...o.board,columns:o.board.columns.filter(p=>p.toLowerCase()!==n.toLowerCase()),columnColors:c}}}),await t().reloadBoard(),t().toast("Column deleted")}catch(o){t().toast("Failed to delete column: "+o.message,"error"),e({columns:s})}},createTask:async n=>{const{columns:a,tasksDirHandle:i,config:r,taskContents:s}=t();if(!i&&!Qt()||!a.length)return null;const o=n||r.board.columns[0]||a[0].name,c=Woe(a),p=a.find(f=>f.name===o)?.tasks.length??0,d={id:c,title:"",checked:!1,dependsOn:[],tags:[],assignee:null,priority:r.fields.priority?r.board.defaultPriority:null,ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",progress:null,frontmatter:{}},m=a.map(f=>f.name===o?{...f,tasks:[...f.tasks,d]}:f);e({columns:m});try{const f={id:c,title:"",status:o,order:p,priority:r.fields.priority?r.board.defaultPriority:"",tags:[],assignee:"",created:new Date().toISOString().slice(0,10),ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",tools:""},h="";await eu(i||null,c,f,h);const k=new Map(s);return k.set(c,{frontmatter:f,subtasks:[],body:h}),e({taskContents:k}),t().toast(`Created ${c.replace(/^t/,"")}`),await t().openDrawer(c),c}catch(f){return t().toast("Failed to create: "+f.message,"error"),e({columns:a}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r}=t();if(!i&&!Qt())return;const s=a.map(p=>({...p,tasks:p.tasks.filter(d=>d.id!==n)}));e({columns:s});const o=new Map(r);o.delete(n);const c=new Map(t().searchMatches);c.delete(n),e({taskContents:o,searchMatches:c});try{await cj(i||null,n),t().toast("Deleted")}catch(p){t().toast("Failed to delete: "+p.message,"error"),e({columns:a})}},archiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!Qt()))try{const{frontmatter:i,body:r}=await Wr(a||null,n);await Foe(a||null,n,{...i,archived:!0},r),t().drawerTaskId===n&&t().closeDrawer(),await t().reloadBoard(),t().toast("Archived")}catch(i){t().toast("Failed to archive: "+i.message,"error")}},unarchiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!Qt()))try{const{frontmatter:i,body:r}=await Wr(a||null,n),s={...i};delete s.archived,await zoe(a||null,n,s,r),await t().reloadBoard(),t().toast("Restored")}catch(i){t().toast("Failed to restore: "+i.message,"error")}},setShowArchives:n=>e({showArchives:n}),setShowMetadata:n=>e({showMetadata:n}),openDrawer:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!Qt()))try{const{frontmatter:i,body:r}=await Wr(a,n),{subtasks:s,bodyWithoutSubtasks:o}=Ds(r),c={frontmatter:i,subtasks:s,body:o,savedAt:Date.now()};e({drawerTaskId:n,drawerData:{frontmatter:i,subtasks:s,body:o},drawerBaseVersion:c,conflictState:null,showConflictModal:!1})}catch(i){t().toast("Failed to open: "+i.message,"error")}},closeDrawer:()=>e({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1}),updateDrawerData:n=>{const{drawerData:a}=t();a&&e({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!n||!a)return;const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await eu(i||null,n,o,s),t().toast("Saved"),e({drawerTaskId:null,drawerData:null});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(c){t().toast("Failed to save: "+c.message,"error")}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!(!n||!a))try{const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};await eu(i||null,n,o,s);const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(s){t().toast("Failed to save: "+s.message,"error")}},setViewMode:n=>{localStorage.setItem("kandown:view",n),e({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),e({density:n})},setFilter:(n,a)=>{if(e(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=t(),o=a,c=i.flatMap(p=>p.tasks.map(d=>d.id));if(r){const p=c.filter(d=>!s.has(d));p.length>0?t().loadTaskContents(p).then(()=>{t().computeSearchMatches(o)}):t().computeSearchMatches(o)}}},clearFilters:()=>e({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>e({commandOpen:n}),setCheatsheetOpen:n=>e({cheatsheetOpen:n}),refreshAgentHook:async()=>{if(!Qt()){e({agentHook:null});return}const n=await $oe();e({agentHook:n?.agentHook??null})},sendTaskToAgent:async n=>{const a=t().agentHook;if(!a){t().toast("Agent hook not configured","error");return}t().toast(`Sending to ${a.label}…`);const i=await Eoe(n);if(i===null){t().toast("Could not reach the daemon","error");return}i.ok?t().toast(`Sent to ${a.label}`):t().toast(i.error||"Agent hook failed","error")},setCurrentPage:n=>e({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=t();if(!a)return;const i=new Map(t().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await Wr(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Ds(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),e({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){e({searchMatches:new Map});return}const{taskContents:a}=t(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=wz(o,r);c.length>0&&i.set(s,c)}e({searchMatches:i})},toast:(n,a="success")=>{const i=++ece;e(r=>({toasts:[...r.toasts,{id:i,message:n,type:a}]})),setTimeout(()=>{e(r=>({toasts:r.toasts.filter(s=>s.id!==i)}))},2500)},dismissToast:n=>e(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=t();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await Wr(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Ds(p);e({drawerData:{frontmatter:c,subtasks:d,body:m},drawerBaseVersion:{frontmatter:c,subtasks:d,body:m,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=zd(i.body,i.subtasks),p={...i.frontmatter,id:s};await eu(r,s,p,c),e({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Overwritten remote changes")}}else e({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{if(Qt()){A2&&clearInterval(A2),A2=setInterval(()=>{t().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=t();if(!n||!a)return;tu.stop(),Ef.forEach(s=>clearTimeout(s)),Ef.clear(),tu.start(n,a);const i=(s,o)=>{const c=Ef.get(s);c&&clearTimeout(c);const p=Math.max(2e3,t().config.notifications.editDebounceMs),d=setTimeout(()=>{Ef.delete(s);const m=t().config;m.notifications.taskEdits&&w2({title:"Task edited",body:`${o} changed on disk.`,config:m})},p);Ef.set(s,d)},r=async s=>{const{tasksDirHandle:o,config:c}=t();if(!o)return;const{frontmatter:p,body:d}=await Wr(o,s),{subtasks:m,bodyWithoutSubtasks:f}=Ds(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},b=d3(h),k=du.get(s);if(!k){du.set(s,b);return}c.notifications.statusChanges&&k.status!==b.status&&w2({title:"Task status changed",body:`${b.title}: ${k.status} → ${b.status}`,config:c});const w=Qoe(k.subtasks,b.subtasks);c.notifications.subtaskCompletions&&w>0&&w2({title:"Subtask completed",body:w===1?`${b.title}: 1 subtask completed.`:`${b.title}: ${w} subtasks completed.`,config:c}),Joe(k,b)&&i(s,b.title),du.set(s,b)};tu.on("configChanged",()=>{t().loadConfig(),t().toast("Settings updated externally","info")}),tu.on("taskChanged",async s=>{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=t();if(await r(s),o===s&&c&&p){const{frontmatter:d,body:m}=await Wr(p,s),{subtasks:f,bodyWithoutSubtasks:h}=Ds(m),b=c,k=JSON.stringify(b.frontmatter)!==JSON.stringify(d),w=b.body!==h,x=JSON.stringify(b.subtasks)!==JSON.stringify(f);if(!k&&!w&&!x)return;let S="none";k&&(w||x)?S="full":k?S="metadata-only":(w||x)&&(S="body-only"),e({conflictState:{taskId:s,type:S,local:b,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:S==="full"})}else t().reloadBoard()}),tu.on("newTaskDetected",async s=>{const{tasksDirHandle:o}=t();if(o){const{frontmatter:c,body:p}=await Wr(o,s),{subtasks:d,bodyWithoutSubtasks:m}=Ds(p);du.set(s,d3({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:m,subtasks:d}))}t().reloadBoard()})}}));l3().then(e=>{fe.setState({recentProjects:e})});nl(Ci);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{nl(fe.getState().config)});function tce(){const e=fe(o=>o.config),t=fe(o=>o.updateConfig),[n,a]=$.useState(!1);if($.useEffect(()=>{a(!0)},[]),!n)return null;const i=e.ui.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.ui.theme,r=i==="dark",s=()=>{const o=r?"light":"dark";t(c=>({...c,ui:{...c.ui,theme:o}}))};return _.jsx(_t.button,{onClick:s,className:r3("relative inline-flex items-center rounded-full transition-all duration-300","border border-black/[0.08] dark:border-white/[0.12]","bg-black/[0.05] dark:bg-white/[0.08]","h-8 w-[52px] px-1"),title:r?"Switch to light mode":"Switch to dark mode",children:_.jsx(_t.div,{className:r3("inline-flex items-center justify-center rounded-full shadow-md",r?"bg-[#1e293b] border border-white/[0.15]":"bg-black border border-black/20","h-[26px] w-[26px]"),animate:{x:r?22:0},transition:{type:"spring",stiffness:500,damping:30},children:_.jsx(_t.div,{initial:{rotate:-90,scale:.5,opacity:0},animate:{rotate:0,scale:1,opacity:1},transition:{duration:.25,ease:"easeOut"},children:r?_.jsx(Zn.Moon,{size:13,className:"text-sky-300"}):_.jsx(Zn.Sun,{size:13,className:"text-amber-400"})},i)})})}function nce(e){const t=cse(e,{stiffness:180,damping:22,mass:.8});return $.useEffect(()=>{t.set(e)},[e,t]),nz(t,a=>Math.round(a).toString())}const u3="0.13.1",ace=({className:e})=>_.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 150 150",className:e,fill:"currentColor",children:[_.jsx("path",{d:"m57.6 64.6 0.1-0.1v-31.3c-0.1-3.5-2.7-5.6-5.7-5.6h-16.3v59.6c0.2-1.3 0.9-2.6 1.7-3.2l20.2-19.4z"}),_.jsx("path",{d:"m87.6 43.8c-3.4 0.1-7.1 1.3-9.9 3.7l-38.5 38.7c-2.1 2.1-3.4 4.8-3.5 7.5v26.7l77.5-76.4v-0.2h-25.6z"}),_.jsx("path",{d:"m108.1 96.4-6 5-22.4-20.8-14.6 14.2 21.1 21.4-5 4.1c-0.6 0.5-0.3 0.9 0.2 0.9h27.6c0.4 0 0.7-0.4 0.7-0.6v-24.8c0-0.7-1.1-0.3-1.6 0.3v0.3z"})]});function ice(){const{t:e}=Rn(),t=fe(V=>V.dirHandle),n=fe(V=>V.isOpen);fe(V=>V.projectName);const a=fe(V=>V.archivedTasks.length),i=fe(V=>V.showArchives),r=fe(V=>V.setShowArchives),s=fe(V=>V.columns),o=fe(V=>V.openFolder),c=fe(V=>V.reloadBoard),p=fe(V=>V.createTask),d=fe(V=>V.setCommandOpen),m=fe(V=>V.viewMode),f=fe(V=>V.setViewMode),h=fe(V=>V.density),b=fe(V=>V.setDensity),k=fe(V=>V.setCurrentPage),w=fe(V=>V.recentProjects),x=fe(V=>V.openRecentProject),S=fe(V=>V.filters),E=fe(V=>V.setFilter),P=fe(V=>V.clearFilters),L=fe(V=>V.config.fields),[j,F]=$.useState(!1),z=$.useRef(null),R=$.useRef(null),M=s.reduce((V,H)=>V+H.tasks.length,0),K=nce(M),J=[];L.priority&&S.priority&&J.push({type:"priority",label:S.priority,value:S.priority}),L.tags&&S.tag&&J.push({type:"tag",label:"#"+S.tag,value:S.tag}),L.assignee&&S.assignee&&J.push({type:"assignee",label:"@"+S.assignee,value:S.assignee});const W=[{label:e("filterBar.ownerAll"),value:""},{label:e("filterBar.ownerHuman"),value:"human"},{label:e("filterBar.ownerAI"),value:"ai"}],B=J.length>0||S.search||L.ownerType&&S.ownerType;return $.useEffect(()=>{if(!j)return;const V=H=>{z.current&&!z.current.contains(H.target)&&F(!1)};return document.addEventListener("mousedown",V),()=>document.removeEventListener("mousedown",V)},[j]),_.jsxs("header",{className:"flex items-center justify-between px-5 h-[64px] border-b border-border bg-card/80 backdrop-blur-xl relative z-10",children:[_.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[_.jsx("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:_.jsxs("button",{onClick:()=>window.history.pushState({},"",window.location.pathname),className:"flex items-center gap-2 cursor-pointer",children:[_.jsx(ace,{className:"w-[34px] h-[34px] dark:text-white text-black"}),_.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),_.jsxs("span",{className:"inline-flex items-center h-5 px-1.5 text-[10.5px] font-semibold text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-500/15 rounded-md",children:["v",u3]})]})}),t&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.jsxs("div",{className:"flex items-center gap-2 px-3 h-9 bg-secondary/60 border border-border rounded-xl min-w-[200px] max-w-[280px] focus-within:border-border-focus focus-within:bg-secondary transition-all",children:[_.jsx(Zn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),_.jsx("input",{ref:R,type:"text",placeholder:e("filterBar.searchPlaceholder"),value:S.search,onChange:V=>E("search",V.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),S.search?_.jsx("button",{onClick:()=>E("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:_.jsx(Zn.X,{size:14})}):_.jsx("kbd",{className:"inline-flex items-center h-5 px-1.5 text-[10px] font-medium text-fg-muted/50 bg-black/[0.04] dark:bg-white/[0.08] rounded border border-black/[0.06] dark:border-white/[0.1]",children:"⌘K"})]}),_.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[_.jsx(ps,{children:J.map(V=>_.jsxs(_t.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>E(V.type,null),className:"inline-flex items-center gap-1 h-6 px-2.5 text-[12px] text-fg bg-black/[0.05] dark:bg-white/[0.1] border border-black/[0.08] dark:border-white/[0.12] rounded-lg hover:bg-black/[0.08] dark:hover:bg-white/[0.15] transition-colors",children:[V.label,_.jsx(Zn.X,{size:10,className:"text-fg-muted/60"})]},V.type+V.value))}),L.ownerType&&_.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:W.map(V=>_.jsx("button",{onClick:()=>E("ownerType",V.value),className:`h-full px-2.5 text-[12px] transition-colors ${S.ownerType===V.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:V.label},V.value))}),B&&_.jsx("button",{onClick:P,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:e("filterBar.clearAll")})]})]}),t&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.jsxs("div",{className:"relative flex-shrink-0",ref:z,children:[_.jsxs("button",{onClick:()=>F(V=>!V),className:"flex items-center gap-1.5 px-2.5 py-1.5 text-[13px] text-fg-muted hover:text-fg hover:bg-black/[0.04] dark:hover:bg-white/[0.06] rounded-lg transition-colors border border-transparent hover:border-black/[0.06] dark:hover:border-white/[0.1]",children:[_.jsx(Zn.Folder,{size:13,className:"text-fg-muted/70"}),_.jsxs("span",{className:"font-medium",children:[".",t.name]}),_.jsx(Zn.ChevronDown,{size:11,className:"opacity-50"})]}),_.jsx(ps,{children:j&&_.jsx(_t.div,{initial:{opacity:0,y:-4,scale:.98},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:-4,scale:.98},transition:{duration:.12},className:"absolute top-full left-0 mt-2 min-w-[240px] glass rounded-xl shadow-[0_16px_48px_rgba(0,0,0,0.5)] overflow-hidden z-50",children:_.jsxs("div",{className:"py-1.5",children:[w.length>0&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:e("header.recentProjects")}),w.map(V=>_.jsxs("button",{onClick:()=>{F(!1),x(V)},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[_.jsx(Zn.Folder,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{className:"truncate",children:V.name}),V.id===t.name&&_.jsx(Zn.Check,{size:12,className:"ml-auto text-emerald-500"})]},V.id)),_.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),_.jsxs("button",{onClick:()=>{F(!1),o()},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[_.jsx(Zn.Plus,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{children:e("header.openFolder...")})]})]})})})]})]})]}),_.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||t?_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[_.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),_.jsx(_t.span,{className:"tabular-nums font-medium",children:K}),_.jsx("span",{children:e("header.tasks")})]}),_.jsxs("div",{className:"flex items-center bg-black/[0.04] dark:bg-white/[0.06] border border-black/[0.06] dark:border-white/[0.1] rounded-xl p-0.5 h-10",children:[_.jsx("button",{onClick:()=>f("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.board"),children:_.jsx(Zn.LayoutBoard,{size:18})}),_.jsx("button",{onClick:()=>f("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.list"),children:_.jsx(Zn.LayoutList,{size:18})})]}),_.jsx(da,{variant:"icon",icon:"Archive",onClick:()=>r(!i),title:i?e("header.backToBoard"):`${e("header.archives")} (${a})`,className:i?"text-accent":""}),_.jsx(da,{variant:"icon",icon:"Density",onClick:()=>b(h==="compact"?"comfortable":"compact"),title:`Density: ${h}`}),_.jsx(da,{variant:"icon",icon:"Settings",onClick:()=>k("settings"),title:e("common.settings")}),_.jsx(tce,{}),_.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),_.jsx(da,{variant:"secondary",icon:"Search",label:e("common.search"),shortcut:"⌘K",onClick:()=>d(!0),title:"Command palette (⌘K)"}),_.jsx(da,{variant:"icon",icon:"Refresh",onClick:c,title:`${e("common.reload")} (R)`}),_.jsx(da,{variant:"primary",icon:"Plus",label:e("common.newTask"),shortcut:"N",onClick:()=>p()})]}):_.jsx(da,{variant:"primary",label:e("common.openFolder"),onClick:o})})]})}/**
117
117
  * @license @tabler/icons-react v3.41.1 - MIT
118
118
  *
119
119
  * This source code is licensed under the MIT license.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kandown",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "File-based Kanban engine backed by Markdown. Zero backend, AI-agent friendly.",
5
5
  "type": "module",
6
6
  "bin": {